Обработка ошибок в ECMAScript

1- Что такое Error?

Для начала рассмотрим пример, в данном примере я вызываю метод объекта, но данный объект на самом деле не имеет этот метод, и происходит ошибка.
hello-error-example.js
console.log("Three");

let myObj = {};

console.log("Two");

console.log("One");

// Объект myObj не имеет метода showMe().
// Но мы вызываем метод showMe().
// И ошибка происходит здесь.
myObj.showMe(); // ==> Error!!!!!!!!!


// Срока кода ниже не будет выполнена.
console.log("Let's go!");
Результат запуска примера:
Вы можете увидеть уведомление ошибки на экране Console, уведомление ошибки очень понятное, на какой строке кода это произошло.
Просмотрите поток программы через следующее изображение.
  • Программа запущена абсолютно нормально в шагах (1), (2), (3), (4)
  • Шаг (5) происходит ошибка, когда вы пытаетесь вызвать метод объекта, хотя этот объект не имеет данный метод.
  • Код в шаге (6) не будет выполнен.

2- Обработка ошибок с помощью try-catch

Ошибки могут произойти во время работы (runtime) программы, может быть неожиданная вами ошибка. Используйте  try-catch чтобы помочь найти и обработать ошибку при происхождении.
Вернитесь к примеру выше, мы добавим try-catch чтобы найти и обработать ошибку.
hello-catch-example.js
console.log("Three");

let myObj = {};

console.log("Two");

console.log("One");

try {
    // Объект myObj не имеет метод showMe().
    // Но мы вызываем метод showMe().
    // И ошибка происходит здесь.
    myObj.showMe(); // ==> Error!

    // Данный код будет пропущен
    console.log("!!!");

} catch (e) {
    console.log("Catched error: " + e);
    console.log("OK continue...");
}

console.log("Let's go!");
И результаты запуска примера:
Изображение ниже объясняет поток (flow) программы:
  • Шаги (1)-(4) абсолютно нормальны.
  • Исключение происходит в шаге (5), кода вы пытаетесь вызвать метод объекта, но объект не имеет данный метод.
  • Он сразу же прыгает выполнять команду в блоке catch, шаг (6) пропускается.
  • Шаг (7), (8) будет выполнен.
  • Шаг (9 будет выполнен.

3- Блок try-catch-finally

Выше мы ознакомились с обработкой исключений через блок  try-catch. Полная обработка ошибки использует  try-catch-finally. Блок  finally всегда выполняется, несмотря не то, происходит ошибка в блоке try или нет.
try {  
   // Сделать что-то здесь.
} catch (e ) {
  
   // Сделать что-то здесь.
} finally  {
   // Блок finally всегда выполняется.
   // Сделать что-то здесь.
}
Пример:
try-catch-finally-example.js
function getGreeting(language) {
    try {
        console.log("Code in try block (*)");

        // Вызвать метод greeting() объекта 'language'.
        // Здесь может выброситься исключение
        // если даный объект не имеет метода greeting()
        let v = language.greeting();

        console.log("Code in try block (**)");

        return v;

    } catch (e) {

        console.log("Code in catch block. Something Error: " + e);

    } finally {
        // Блок 'catch' будет выполнен.
        console.log("Code in finally block");
    }

    return " !! ";
}

// ----------------------- TEST ---------------------------------
// Test 1:
console.log("----- Call getGreeting(null) -----");

let v1 = getGreeting(null);
console.log("Greeting: " + v1);


// Test 2:
console.log("------ Call getGreeting(language) ------");

let language = new Object();

language.greeting = function() {
    return "Hello Everybody";
}

let v2 = getGreeting(language);

console.log("Greeting: " + v2);
Запуск примера:
Изображение ниже иллюстрирует поток программы при происхождении ошибки в блоке  try, блок  finally всегда выполняется.
Изображение ниже иллюстрирует поток программы если нет ошибки в блоке  try. В данном случае блок  finally выполняется сразу перед выполнением команды  return блока  try.

4- Built-in Errors

ECMAScript имеет встроенные классы для представления ошибки, ниже является их иерархия (heirachy).

RangeError

RangeError выбрасывается если вы используете число вне разрешаемого диапазона.
error-RangeError-example.js
let num = 1;
try {

  // A number cannot have 500 significant digits
  num.toPrecision(500); // ==> RangeError!!
 
}
catch(err) {
  console.log(err.name);
  console.log(err);
}

ReferenceError

ReferenceError выбрасывается (throw) если вы используете переменную, которая не была объявлена.
error-ReferenceError-example.js
var x;
try {
  x = y + 1;   // y cannot be referenced (used)
}
catch(err) {
   console.log("Error Name: "+ err.name);
   console.log(err);
}

SyntaxError

SyntaxError выбрасывается (throw) если вы пытаетесь оценить (evaluate) код содержащий ошибку синтаксиса.
error-SyntaxError-example.js
try {
  let x;
  eval(" x  = 'Hello  ");   // Missing ' will produce an error
}
catch(err) {
  console.log("Error Name: " + err.name);
  console.log(err);
}

TypeError

TypeError выбрасывается (throw) если вы используете значение неожидаемого вида. Например ошибка происходит, когда вы вызываете метод объект, и объект не имеет данный метод.
error-TypeError-example.js
var num = 1;
try {
  num.toUpperCase(); // Number has no method toUpperCase()
}
catch(err) {
  console.log("Error Name: " + err.name);
  console.log(err);
}
 

URIError

URIError выбрасывается (throw) если вы используете символы недопустимые в функции  URI:
error-URIError-example.js
try {
  decodeURI("%%%");   // You cannot URI decode percent signs
}
catch(err) {
  console.log("Error Name: " + err.name);
  console.log(err);
}
Смотрите так же

5- Выбросить Error

ECMAScript позволяет вам выбросить (throw) нечто во время работы программы, программа будет считать что произошла ошибка.
throw-any-example.js
console.log(" -------- Test throw any object ------------");

try {
   let myObj = {};
   throw myObj;
} catch(e)  {
   console.log("Catch error: ");
   console.log(e);
}

console.log(" -------- Test throw a Symbol ------------");

try {
   let mySymbol = Symbol();

   throw mySymbol;
} catch(e)  {
   console.log("Catch error: ");
   console.log(e);
}

console.log(" -------- Test throw a Number ------------");

try {
   let myNumber = 100;

   throw myNumber;
} catch(e)  {
   console.log("Catch error: ");
   console.log(e);
}

console.log(" -------- Test throw a String ------------");

try {
   let myString = "Some error";

   throw myString;
} catch(e)  {
   console.log("Catched error: ");
   console.log(e);
}
 
Обычно вы используете класс  Error чтобы создать объект ошибки. Другие классы как  SyntaxError, InternalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError так же можно использовать в подходящем контексте.
Объект ошибки создается через класс  Error (или его подклассы), при выбрасывании (throw) он будет содержать важную информацию как файл из-за которого произошла ошибка, местоположение ошибки, и информацию помогающую вам найти ошибку.
throw-error-example.js
console.log("Three");

// Create an Error
let myError = new Error("Something error!");

console.log("Two");

// Throw it!
throw myError;

console.log("One");
В простом случае вы можете выбросить что угодно, не объект класса Error (Или его подклассы). Но при нахождении ошибок данного вида вы не сможете имет информация как файл из-за которого происходит ошибка, местоположение происхождения ошибки,...
throw-string-error-example.js
console.log("Three");

try {

  console.log("Two");

  // Throw a String!
  throw "Some error!!";

} catch(e)  {
   console.log("typeof e = " + (typeof e));
   // Log the error
   console.log(e); // Some error!!
}

console.log("One");
В  ECMAScript, каждый блок  try имеет соответствующий блок  cache. Но в блоке  try могут произойти разные виды ошибок, в данном случае вам нужно проверить ошибку найденную в блоке catch чтобы предоставить подходящую обработку для каждого вида ошибки.
catch-complex-example.js
let err = new Error("My Error");
let rangeErr = new RangeError();
let evalErr = new EvalError("My Eval Error");

// A random value in [0.. 9]
let randomValue = Math.floor(Math.random() * 10);

// [0,1,2,3]
let random0123 = randomValue % 4;

console.log("random0123 = " + random0123);

try {
   if(random0123 == 0) {
      throw err;
   } else if(random0123 == 1){
      throw rangeErr;
   } else if(random0123 == 2)  {
      throw evalErr;
   } else if(random0123 == 3)  {
      throw "A String Error";
   }

} catch(e)  {
   console.log("typeof e = " + (typeof e));// 'object' or 'string'

   if(e instanceof RangeError) {
      console.log("--> RangeError!!");
   } else if(e instanceof EvalError) {
      console.log("--> EvalError!!");
   } else if(e instanceof Error) {
      console.log("--> Error!!");
   } else if (typeof e == "string"){
      console.log("--> String Error!!");
   } else  {
      console.log("--> Error!!");
   }

   console.log(e);
}

6- Свойства Error

В  ECMAScript, ошибка которую вы нашли может быть  "Error object" или любой вид данных. Если это  "Error object" у вас будет важная информация как название файла создавшая ошибку, местоположение ошибки, Stack Trace,..
Есть некоторые важные свойства ( property) класса  Error:
  • name: Название ошибки.
  • message: Содержание ошибки.
  • stack (Readonly): текст содержащий информацию, помогающая вам найти местоположение ошибки.
error-properties-example.js
// Create an Error
let myError = new Error();

myError.name = "MyError";
myError.message = "My Error String";


try {

  throw myError;

} catch(err)  {

  console.log("Error Name: " + err.name);
  console.log("Error Message: " + err.message);
  console.log("Type of err.stack: " + (typeof err.stack));

  console.log("--- Stack Trace: ---");  
  console.log(err.stack);
}

 

7- Перевыбросить ошибку (Re-throw Error)

При обработке исключения вы можете схватить это исключение и обработать или перевыбросить (rethrow) его наружу.
rethrow-example.js
function checkScore(score) {
    if (score < 0 || score > 100) {
        throw "Invalid Score " + score;
    }
}

function checkPlayer(name, score) {

    try {

        checkScore(score)
    } catch (e) {
        // Сделать что-то с exception
        console.log("Something invalid with player: " + name + " >> " + e);

        // Перевыбросить наружу.
        throw e;
    }

    console.log("OK Player " + name + " has score: " + score );
}



// --------------- TEST --------------
checkPlayer("Tom", 90);

checkPlayer("Jerry", -10);
Например, схватить исключение и перевыбросить (rethrow) другое исключение.
rethrow-example2.js
function checkScore(score) {
    if (score < 0 || score > 100) {
        throw "Invalid Score " + score;
    }
}

function checkPlayer(name, score) {

    try {

        checkScore(score)
    } catch (e) {
        // Сделать что-то с exception
        console.log("Something invalid with player: " + name + " >> " + e);

        // Потом выбросить другое исключение
        throw ("Score " + score +" invalid for player " + name);
    }

    console.log("OK Player " + name + " has score: " + score );
}



// --------------- TEST --------------
checkPlayer("Tom", 90);

checkPlayer("Jerry", -10);

8- Исключения при схватке ошибок

В ECMAScript имеются ситуации, когда вы думаете возникнет ошибка, но это не происходит. Например деление на 0 не создает ошибку, результат возвращает  Infinity или  -Infinity.
ex-Infinity-example.js
console.log( typeof Infinity ); // number

let a = 1 / 0;

console.log(a); // Infinity

let b = -1 / 0;

console.log(b); // -Infinity
 
Возьмите значение не число, чтобы поделить на число, результатом будет  NaN ( Not a Number).
ex-NaN-example.js
console.log( typeof NaN ); // number
console.log( isNaN(1) ); // false
console.log( isNaN( NaN ) ); // true

let a = "A String" / 2;

console.log(a); // NaN

let obj = {};
let b = obj / 2;

console.log(b); // NaN
Вы можете получить доступ в элемент с индексом любого массива  (Array) не вызывая ошибку.
ex-array-index-example.js
let myArray = [1, 100, 20];

console.log( myArray[1]); // 100

console.log( myArray[10] ); // undefined

console.log( myArray[-10] ); // undefined