ITKUP
Личный блог на тему разработки, IT. Учусь, записываю, анализирую.

JavaScript (конспект): Базовый минимум

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

1. Типы данных

В языке JavaScript имеется восемь типов данных, которые в свою очередь можно поделить на:
  • примитивные (хранят одно значение);
  • сложные/комплексные (хранят несколько значений).

Примитивные:

  • String (строка);
  • Number (числовое значение);
  • BigInt (представление очень больших целых чисел);
  • Boolean (логическое значение, true или false);
  • Undefined (специальное значение; указывает, что значение не установлено);
  • Null (специальное значение; указывает на отсутствие значения);
  • Symbol (уникальное значение; применяется для обращения к свойствам сложных объектов).

Сложные/комплексные:

  • Object (комплексный объект).


// typeof - встроенный оператор, возвращающий строку с  типом

typeof 42                  // "number"
typeof "hello"         // "string"
typeof true               // "boolean"
typeof undefined   // "undefined"
typeof null                // "object" — это исторический баг!


Далее рассмотрим каждый тип отдельно.


String (строка)

String (далее "строка") должна быть заключена в кавычки. Кавычки используются трех видов:

  1. Двойные (например: "Строка");
  2. Одинарные (например: 'Строка');
  3. Обратные (например: `Строка`).

"Двойные" и "одинарные" - не имеют разницы. "Обратные" имеют расширенную функциональность: позволяют встраивать в строку выражения (выполнение интерполяции; заключая их в ${…}) и определять многострочный текст.


// Многострочный текст
let text = 
`Много-много текста.
Очень много...`;
alert( text );

let name = "Иван";
// Вставим переменную
alert( `Привет, ${name}!` );     // Привет, Иван!

// Вставим выражение
alert( `результат: ${1 + 2}` );     // результат: 3


Вычисляется результат выражения внутри ${…} и встраивается в строку. Можем добавлять внутрь всё, что угодно.

Важный момент: тип закрывающей кавычки должен совпадать с открывающей.

Для вставки кавычек внутри строки - нужно обязательно их экранировать.


const title = "ООО \"Заря\"";



Number (числовое значение)

Данный тип представляет в JavaScript как целые, так и дробные числа (числа с плавающей точкой). Также существуют «специальные числовые значения», относящиеся к этому типу данных: Infinity, -Infinity и NaN.

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

NaN - обозначает вычислительную ошибку (результат неправильной или неопределённой математической операции). Если в математическом выражении есть NaN - оно распространяется на весь результат.

В JavaScript есть возможность определять числа в двоичной, восьмеричной и шестнадцатеричной системах.

Для числа в двоичной системе указывается префикс 0b:


const numVal = 0b1011;    // число 11 в двоичной системе
console.log(numVal);         // 11


Для числа в восьмеричной системе указывается префикс 0o:


const numVal = 0o11;    // число 9 в восьмеричной системе
console.log(numVal);   
// 9


Для числа в шестнадцатеричной системе указывается префикс 0x:


const numVal = 0xff;    // число 255 в шестнадцатеричной системе
console.log(numVal);   // 255



BigInt

Тип Number имеет ограничения диапазона чисел (не больше, чем 9007199254740991 и не меньше чем -9007199254740991). Для работы с числами, выходящие за эти рамки был придуман специальный тип BigInt (добавлен в последних стандартах JavaScript).


Boolean (логическое значение, true или false)

Данный тип может принимать только два значения: true (истина) или false (ложь).


Undefined

Специальное значение, имеет единственное значение undefined. Означает «значение не было присвоено».


Null

Специальное значение, имеет единственное значение null. Означает «пусто».


Symbol 

Данный тип (символ) используется для создания уникальных идентификаторов в объектах.


Object 

В объектах хранят коллекции данных, более сложные структуры. 


const simpleObj = {
    id: 1,
    title: "Наименование"
};


Неявные преобразования типов:


// Строка + число => строка
console.log('5' + 3);   
// "53"

// Число + строка
=> строка
console.log(3 + '5');   
// "35"

// Но вычитание
=> число
console.log('9' - 5);   
// 4

// Логические преобразования
if ('hello') { }    // true (непустая строка)
if ('') { }              // false (пустая строка)
if (0) { }              // false
if (1) { }              // true

// Явное приведение
Boolean(0)           // false
Number('123')    // 123
String(123)          // "123"


2. Объявление переменных (let/const)

Переменная – это «именованное хранилище» данных. Она может быть как изменяемая (просто "переменная"), так и неизменяемая ("константа").


Обычная переменная объявляется при помощи ключевого слова let. Значение данного типа переменной можно спокойно изменять в дальнейшем:


let ExampVal = 0;

console.log("ExampVal:", ExampVal);    // Выведет "ExampVal: 0"

ExampVal = 1;
console.log("ExampVal:", ExampVal);   
// Выведет "ExampVal: 1", т.к. мы переопределили значение.


Используя const, мы определяем константу, значение которой неизменно в дальнейшем.


const MYCONST = "Yes";

MYCONST = "No";    // Не сработает, ошибка


Важный момент: const запрещает переприсваивание переменной, но не запрещает изменять содержимое объекта или массива.


const user = {
    name: "Иван",
    age: 25
};

user = { name: "Петр" };   
// TypeError, переприсваивание не выполнить
user.name = "Петр";         
// Сработает, свойство изменять можно
user.city = "Москва";       
// Сработает, добавлять свойства можно


3. Функции (стрелочные, обычные)

Функция в JavaScript - это блок кода, предназначенный для выполнения определенной конкретной операции.

Правило объявления функции, в общем виде, выглядит следующим образом: сперва идёт ключевое слово function, после него имя функции, затем список параметров в круглых скобках через запятую и код (тело) функции.


// Объявление функции
function SayHiWorld(UserName) {
    console.log(`Hi ${UserName} and all world!`);
}

// Вызов функции
SayHiWorld("Петя");


В примере выше указан пример написания "обычной" функции. Кроме этого существует такое понятие, как "стрелочная функция".

Стрелочные функции (от англ. "Arrow Functions") позволяют сократить запись определения обычных функций. Они определяются с помощью оператора =>, перед которым в скобках идут параметры функции, а после - собственно тело функции.


// Объявление функции
const SayHiWorld = (UserName)=> console.log(`Hi ${UserName} and all world!`);

// Вызов функции
SayHiWorld("Петя");


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


// Объявление функции
const GetText = (UserName)=> `Hi ${UserName} and all world!`;

// Вызов функции
GetText("Петя");    // Вернет строку "Hi Петя and all world!"


В случае, если функции должен в качестве результата вернуть объект, то сам объект (в теле функции) следует заключать в круглые скобки.


// Объявление функции
const 
GetObject = (ID, Name, Age)=> ({id: ID, name: Name, age: Age});

// Вызов функции
GetObject(1, "Таня", 24);    
// Вернет объект {id: 1, name: "Таня", age: 24}


Главные отличия обычной функции от стрелочной:

  • обычная имеет свой this, стрелочная же берет из внешнего контекста;
  • обычную можно использовать как конструктор (new);
  • в обычной функции существуют arguments, в стрелочной их нет;


// Обычная функция имеет свой arguments
function sum() {
    console.log(arguments);    // [1, 2, 3] (псевдомассив)

    let total = 0;
    for (let i = 0; i < arguments.length; i++) {
        total += arguments[i];
    }
    return total;
}
sum(1, 2, 3);    // 6; отработает нормально


// Стрелочная функция - arguments нет
const sum = () => {
    console.log(arguments);    // ReferenceError: arguments is not defined
};
sum(1, 2, 3);    // будут проблемы


  • в обычной функции существует специальное свойство, которое доступно внутри функций и конструкторов new.target, в стрелочной функции его нет (оно показывает: как была вызвана функция - через new или как обычная функция и какой конструктор был вызван через new;  это "детектор вызова через new").


// Обычная функция - все сработает
function User(name) {
    console.log(new.target === User);    // true при вызове через new
    console.log(new.target);                      // [Function: User]
    this.name = name;
}

new User("Иван");


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


ОператорНазваниеПример
&&И (AND)true && true    // true
|| ИЛИ (OR)true || false    // true
!НЕ (NOT)!true    // false
!!НЕ (NOT), с приведением к boolean; это не отдельный оператор, а двойной !// Быстрое преобразование любого значения в true/false

!!1 // true
!!0 // false
!!"hello" // true
??Нулевое слияние (Nullish coalescing)null ?? "default" "default"

5. Циклы

Циклы бывают следующих типов:
  • for - цикл, используется для известного количества итераций;
  • while - цикл с проверкой условия перед каждой итерацией;
  • do...while - цикл с проверкой условия после каждой итерации ("выполнить минимум один раз");
  • for...of - цикл для перебора значений массива/итерируемых объектов ES6;
  • for...in - цикл для перебора ключей объекта;
  • forEach - метод массива (не совсем цикл, но часто используется, нельзя прервать).

Рассмотрим примеры.


for


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


while


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


do...while


// Проверка после выполнения
let i = 10;
do {
    console.log(i);   
// Выполнится 1 раз (10)
    i++;
} while (i < 5);

// while не выполнился бы ни разу
let j = 10;
while (j < 5) {
    console.log(j);   
// Не выполнится
    j++;
}


for...of

Перебор значений итерируемых объектов (ES6+), работает с:

  • Массивы;
  • Строки;
  • Map, Set;
  • NodeList (DOM элементы);
  • arguments;
  • Любые итерируемые объекты.


// Массив
const fruits = ['яблоко', 'банан', 'апельсин'];
for (const fruit of fruits) {
    console.log(fruit);    // яблоко, банан, апельсин
}

// Строки
for (const char of "hello") {
    console.log(char);    // h, e, l, l, o
}

// Set
const unique = new Set([1, 2, 3, 2, 1]);
for (const value of unique) {
    console.log(value);    // 1, 2, 3
}

// Map
const map = new Map([['a', 1], ['b', 2]]);
for (const [key, value] of map) {
    console.log(key, value);    // a 1, b 2
}


for...in


// Перебор ключей объекта
const user = {
    name: 'Иван',
    age: 25,
    city: 'Москва'
};

for (const key in user) {
    console.log(key, user[key]);   
 // name Иван, age 25, city Москва
}


forEach


const fruits = ['яблоко', 'банан', 'апельсин'];
fruits.forEach(function(item, index, array) {
    console.log(item, index);
});

// Стрелочная функция
fruits.forEach((item, index) => {
    console.log(`${index}: ${item}`);
});


Управление циклом

break — полный выход из цикла


for (let i = 0; i < 10; i++) {
    if (i === 5) break;
    console.log(i);    // 0,1,2,3,4
}

// Поиск элемента
const numbers = [1, 3, 5, 7, 9];
let target = 7;
for (let i = 0; i < numbers.length; i++) {
    if (numbers[i] === target) {
        console.log(`Нашли ${target} на позиции ${i}`);
        break;   
// выходим, дальше искать смысла нет
    }
}


continue — пропуск текущей итерации


for (let i = 0; i < 5; i++) {
    if (i === 2) continue;
    console.log(i);    // 0,1,3,4
}

// Пропускаем четные числа
for (let i = 0; i < 10; i++) {
    if (i % 2 === 0) continue;
        console.log(i);    // 1,3,5,7,9
}


return в циклах внутри функций


function findFirstEven(numbers) {
    for (let i = 0; i < numbers.length; i++) {
        if (numbers[i] % 2 === 0) {
            return numbers[i];   
// return выходит из функции
        }
    }
    return null;
}

console.log(findFirstEven([1, 3, 5, 6, 7]));   
// 6

6. Event Loop

JavaScript - однопоточный язык, т.е. выполнять он может только одну операцию "одновременно". Event Loop - это механизм, позволяющий выполнять "асинхронные" операции/колбеки (но на самом деле JavaScript тоже выполняет их синхронно). Асинхронные операции (такие как setTimeout, fetch, чтение файлов) запускаются браузером/Node.js. Когда они завершаются, их колбэки попадают в очередь макрозадач (или микрозадач) и ждут своей очереди в Event Loop.

Все операции выполняются поочередно. Правило исполнения (простыми словами):

  1. Синхронный код.
  2. ВСЕ микрозадачи (Promise).
  3. Задачи отрисовки.
  4. ОДНА макрозадача (setTimeout).
  5. И снова (на начало, п.2) по кругу.

Важный момент: задачи отрисовки (рендер) происходит после выполнения всех микрозадач, но перед следующей макрозадачей. Браузер может объединять несколько изменений DOM за один рендер.

Event Loop - это бесконечный цикл, который постоянно проверяет, свободен ли Call Stack, и если да — забирает задачи из очередей (микрозадач, макрозадач, отрисовки).

! ЗАПОМИНАЕМ !

Стек вызовов (от англ. Call Stack) - это специальный механизм для интерпретатора JavaScript в веб-браузере для отслеживания текущего местонахождения интерпретатора в скрипте, который вызывает несколько функций. Это некая структура данных, устроенная по принципу LIFO (Last In, First Out — последним вошёл, первым вышел). Определяет какая из функций выполняется на данный момент, какие функции вызываются изнутри этой (выполняемой) функции, какая будет вызвана следующей и т. д. А именно:

  • При вызове скриптом функции, интерпретатор добавляет её в стек вызовов (и далее начинает её обработку).
  • Любые вызванные этой функцией функции, добавляются в стек вызовов и выполняются, как только происходит их вызов.
  • При завершении выполнения основной функции, интерпретатор снимает её со стека вызовов и возобновляет выполнение кода в списке основного кода (с той точки, на которой остановился до этого).
  • Если стек занимает больше места, чем ему было присвоено, это приводит к ошибке переполнения стека ("stack overflow" error).


Синхронный код выполняется сразу в Call Stack.
Асинхронные операции (таймеры, запросы) запускаются средой (браузер/Node.js). По завершении их колбэки помещаются в очереди.

Термины, простыми словами:

  • Call Stack - то, что выполняется сейчас.
  • Microtask Queue - то, что выполнится перед следующей макрозадачей.
  • Task Queue (Macrotask) - то, что выполнится по очереди, по одному.
  • Rendering - отрисовка страницы (между макрозадачами).

Микрозадача - в основном then у промисов, а также Promise.then/catch/finally, MutationObserver, queueMicrotask().
Intersection Observer.

Макрозадача - это колбэки от: setTimeout/setInterval, событий UI (клики, скролл), сетевых запросов (fetch, XHR), чтения файлов.

Задача отрисовки -  задача, связанная с отрисовкой и обновлением контента страницы.


console.log('1');                                                              // синхронно
setTimeout(() => console.log('2'), 0);                     // макрозадача
Promise.resolve().then(() => console.log('3'));   // микрозадача
console.log('4');                                                              // синхронно

// Вывод: 1, 4, 3, 2


Пример порядка исполнения:


console.log('1');                                                       // синхронно

setTimeout(() => console.log('2'), 0);              // макрозадача

Promise.resolve().then(() => {
    console.log('3');                                                  // микрозадача
});

document.body.style.background = 'red';    // запрос на отрисовку

console.log('4');                                                     // синхронно

// Вывод: 1, 4, 3, 2
// Отрисовка произойдёт после 3, перед 2 (но консоль это не показывает)


Важный момент: один глобальный Event Loop обрабатывает все асинхронные операции независимо от областей видимости.

7. Замыкания

Замыкание — это "особая" функция, у которой есть доступ к области видимости, которую формирует внешняя (по отношению к ней) функция даже после того, как эта (внешняя) функция завершила свою работу. В замыкании могут храниться переменные, объявленные во внешней функции и переданные ей аргументы.


function createMultiplier(inpArg) {
    return function(i) {
        return i * inpArg;
    }
}

const double = createMultiplier(2);
const triple = createMultiplier(3);

console.log(double(5));   
// 10
console.log(triple(5));       
// 15

//  У каждого вызова createMultiplier() создаётся своё собственное замыкание со своим "внутренним" значением, они не мешают друг другу

! ЗАПОМИНАЕМ !

Контекст выполнения - абстрактное окружение, в котором вычисляется и выполняется код JavaScript. Выполнение глобального кода происходит внутри глобального контекста выполнения, код функции выполняется внутри контекста выполнения функции. В один промежуток времени может выполняться код лишь в одном контексте выполнения, т.к. JavaScript - однопоточный язык (управление этими процессами ведётся с использованием Call Stack). Текущий контекст выполнения всегда будет в верхней части стека.


Лексическое окружение - это структура данных, хранящая сведения о соответствии идентификаторов и переменных («идентификатор», в данном контекста, - это имя переменной или функции, а «переменная» — это ссылка на объект (сюда входят и функции) или значение примитивного типа).

Лексическое окружение состоит из двух компонентов:

  1. Запись окружения (от англ. "Environment Record") — место, где хранятся объявления переменных и функций.
  2. Ссылка на внешнее окружение (от англ. "Reference To The Outer Environment") - ссылка, позволяющая обращаться к внешнему (родительскому) лексическому окружению.

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


Важный момент: замыкание хранит ссылку, не копию.


// Проблемный код (var; функции делят один i, т.к. var не имеет блочной области)
function createFunctionsVar() {
    let result = [];
    for (var i = 0; i < 3; i++) {    // var, не let!
        result.push(function() {
            console.log(i);
        });
    }
    return result;
}

const funcsVar = createFunctionsVar();
funcsVar[0]();    // 3 (ожидаем 0)
funcsVar[1]();    // 3 (ожидаем 1)
funcsVar[2]();    // 3 (ожидаем 2)


// Рабочий код (let; создаёт новую переменную i для каждой итерации)
function createFunctions() {
    let result = [];
    for (let i = 0; i < 3; i++) {
        result.push(function() {
            console.log(i);
        });
    }
    return result;
}

const funcs = createFunctions();
funcs[0]();    // 0 (как и ожидалось)
funcs[1]();    // 1 (как и ожидалось)
funcs[2]();    // 2 (как и ожидалось)

! ЗАПОМИНАЕМ !

Термины, простыми словами:

  • Замыкание - это функция, которая «помнит» переменные из места своего рождения.
  • Контекст выполнения - «окружение» для выполнения кода (глобальное или функции).
  • Лексическое окружение - это объект с переменными + ссылка на «родителя».

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

Самые распространенные структуры данных в JavaScript - это Object и Array. Бывают случаи, когда для работы необходим не весь объект или массив, а "отдельные его составляющие". Намного удобнее не перетягивать везде весь объект, а вытащить из него отдельные интересующие нас части и работать с ними как с самостоятельными переменными.

! ЗАПОМИНАЕМ !

Деструктуризация (или деструктурирующее присваивание) –  специальный синтаксис, позволяющий нам «разобрать» массивы или объекты на несколько самостоятельных переменных.



// Пример разбиения массива на отдельные переменные
let exArr = ["Василий", "Теркин"];
// деструктурирующее присваивание: записывает firstName = arr[0] и surname = arr[1]
let [firstName, surname] = exArr;

console.log(firstName);    // Василий
console.log(surname);       // Теркин


// Пример разбиения объекта на отдельные переменные

let exTwoArr = {
    fName: "Анна",
    sName: "Каренина"
};
let { fName, sName } = exTwoArr;

console.log(fName);     // Анна
console.log(sName);    // Каренина



// "Практический" пример сочетания со split (метод, возвращающий массив)
let [FName, SName] = "Федор Достоевский".split(' ');

alert(FName);     // Федор
alert(SName);     // Достоевский


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


Ненужные элементы можно "отбросить", т.е. намеренно пропустить и не объявлять под это целую переменную, которую потом мы не планируем никак использовать. Для этого следует использовать дополнительные запятые.



let exArr = ["Красноярск", "ул. Мира", "дом 3"];
let [city, , house] = exArr;

console.log(city);         
// Красноярск
console.log(house);    // дом 3


Однако, оставшиеся значения можно собрать в отдельную переменную (если того требует задача), используя оператор «остаточные параметры» – троеточие ("..."):


// Вариант работы с массивом
let [firstName, secondName, ...otherNames] = ["Иван", "Сергей", "Оля", "Валя", "Николай"];


console.log(firstName);          // Иван
console.log(secondName);    // Сергей

// otherNames - массив оставшихся элементов, начиная с 3-го
console.log(otherNames[0]);    // Оля
console.log(otherNames[1]);    // Валя
console.log(otherNames[2]);    // Николай


// Вариант работы с объектом
let options = {
    title: "Title text val",
    height: 600,
    width: 800
};

let {title, ...otherVals} = options;

// title=> "
Title text val", otherVals=> {height: 600, width: 800}
console.log(otherVals.height);    // 600
console.log(otherVals.width);      // 800


Используя деструктурирующее присваивание, можно выполнять обмен значений для двух (и более) переменных. Создавая временный массив из двух переменных и немедленно деструктурируем его (в порядке замены). Данный трюк используется в методе сортировки "пузырем".


let aVal = "Din";
let bVal = "Don";

[aVal, bVal] = [bVal, aVal];

console.log(aVal);   
// Don
console.log(bVal);   
// Din


Также работает схема со значением по умолчанию.


let user = {
    name: "Толя",
    age: 10
};

const { country = "Россия", name } = user;
console.log(country);   
// "Россия" (значения не было, сработало значение по умолчанию)
console.log(name);         
// "Толя" (значение было)


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


const product = {
    title: "Ноутбук",
    price: 50000,
    specs: {
        ram: "16GB",
        ssd: "512GB"
    }
};

const { specs: { ram, ssd } } = product;
console.log(ram);   
//  16GB
console.log(ssd);     
// 512GB


Еще пара важных/интересных примеров.


// Переименование переменных
const user = { name: "Анна", surname: "Каренина" };
const { name: firstName, surname: lastName } = user;

console.log(firstName);    // Анна
console.log(lastName);     // Каренина


// Деструктуризация в параметрах функции
function UserCard({ name, age, city = "Не указан"}) {
    console.log(`${name}, ${age} лет, г. ${city}`);
}

UserCard({ name: "Иван", age: 25 });                                       // Иван, 25 лет, г. Не указан
UserCard({ name: "Мария", age: 30, city: "Москва" });    // Мария, 30 лет, г. Москва


// Осторожно! Если переменные уже объявлены - будет ошибка!
let a;
{ a } = { a: 10 };    // Синтаксическая ошибка: фигурные скобки в начале строки воспринимаются как блок кода

// Правильно - обернуть ВСЁ выражение в круглые скобки
({ a } = { a: 10 });
console.log(a);    // 10


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

Обработка ошибок в JavaScript основывается на перехвате исключений и управлении асинхронными состояниями. Основные подходы включают классические блоки try...catch, работу с промисами и события глобального перехвата.


try…catch
Данная конструкция состоит из двух основных блоков:

  • try,
  • catch.


try {
   
// исполняемый код, который "пытаемся" выполнить
} catch (err) {
    // обработка ошибки
}


В случае, если детали ошибки нам не интересны - можно их опустить (такая возможность была добавлена относительно недавно, для старых браузеров может понадобиться полифил). Запись "catch(err) {..." просто заменяется на "catch {...".

Принцип работы:

  • Выполняется код внутри блока try {...}.
  • Если в нем нет ошибок, то следующий за ним блок catch(err) игнорируется.
  • В случае возникновения ошибки: выполнение try прерывается, переходим в начало catch(err). Переменная err (можно использовать любое наименование) содержит объект ошибки с подробной информацией.


Важный моментtry..catch работает только для ошибок, возникающих во время выполнения кода, иначе говоря, код должен быть корректным. Он не сработает, если внутри него есть синтаксическая ошибка (например: не совпадающее количество скобок). JavaScript движок сперва читает код, а затем исполняет его. Ошибки, возникающие во время "чтения", называются ошибками парсинга (их нельзя обработать изнутри этого кода).


// Пример корректной обработки ошибки
try {
    const data = JSON.parse("{невалидный json}");
    console.log("Эта строка не выполнится");
} catch (err) {
    console.log("Ошибка:", err.message);
}


Ошибки можно генерировать самостоятельно (например нужно проверить наличие в объекте чего-либо). Для этих целей используется оператор throw.


throw <объект ошибки>


В качестве объекта ошибки можно передавать что угодно, но лучше передавать Object со свойствами name и message (так мы обеспечим совместимость со стандартными ошибками).

Для "стандартных" ошибок в JavaScript есть много конструкторов: Error, SyntaxError, ReferenceError, TypeError и другие.


let error_1 = new Error(message);
let error_2 = new SyntaxError(message);
let error_3 = new ReferenceError(message);


// Для "встроенных" ошибок (не для любых объектов), name – это  имя конструктора, а message - берётся из аргумента
let error = new Error("Тут ошибка!");

console.log(error.name);           // Error
console.log(error.message);    // Тут ошибка!


Блок catch отлавливает все ошибки из try, не только те, которые мы "потенциально предполагаем" и готовы их обрабатывать.

Обрабатываться должны только те ошибки, которые известны, остальные нужно «пробрасывать». Логика следующая:

  1. Получаем все ошибки в блоке catch;
  2. Анализируем тип ошибки (объект ошибки err);
  3. Если не знаем как обрабатывать данный тип, тогда делаем throw err.


let exampJson = '{ "name": "Диего" }';

try {
    let user = JSON.parse(exampJson);

    if (!user.secondName) {
        throw new SyntaxError("Отсутствует искомый параметр!");
    }

    erExampFunc();   
// неожиданная ошибка
    console.log(user.secondName);
} catch(err) {
    if (err.secondName == "SyntaxError") {
        alert( "JSON Error: " + err.message );
    } else {
        throw err;    // проброс неучтенной ошибки
    }
}

Cookie-файлы
Настройка cookie-файлов
Детальная информация о целях обработки данных и поставщиках, которые мы используем на наших сайтах
Аналитические Cookie-файлы Отключить все
Технические Cookie-файлы
Другие Cookie-файлы
Мы используем файлы Cookie для улучшения работы, персонализации и повышения удобства пользования нашим сайтом. Продолжая посещать сайт, вы соглашаетесь на использование нами файлов Cookie. Подробнее о нашей политике в отношении Cookie.
Понятно Подробнее
Cookies