Heim  >  Artikel  >  Web-Frontend  >  Analyse von Fehlerausnahmen in JavaScript (mit Beispielen)

Analyse von Fehlerausnahmen in JavaScript (mit Beispielen)

不言
不言nach vorne
2019-04-04 11:22:422485Durchsuche

Der Inhalt dieses Artikels befasst sich mit der Analyse von Fehlern und Ausnahmen in JavaScript. Ich hoffe, dass er für Freunde in Not hilfreich ist du hilfst.

Mein Rat ist, Fehler nicht zu verbergen, sondern mutig genug zu sein, sie zu verwerfen. Niemand sollte sich schämen, wenn ein Fehler im Code zum Absturz des Programms führt. Wir können das Programm unterbrechen und den Benutzer von vorne beginnen lassen. Fehler sind unvermeidlich, entscheidend ist, wie man damit umgeht.

JavaScript bietet einen Fehlerbehandlungsmechanismus. Fehler sind ungewöhnliche Vorfälle, die den normalen Programmablauf beeinträchtigen. Und niemand kann das Programm fehlerfrei halten, wenn Sie nach dem Online-Gehen auf einen speziellen Fehler stoßen, wie können Sie das Problem dann schneller lokalisieren? Das ist es, worüber unser Thema sprechen muss.

Im Folgenden werden die Grundkenntnisse von JavaScript-Fehlern beschrieben, wie Ausnahmen abgefangen und erfasst werden und wie Fehler bequem online gemeldet werden können. Ich habe auch einige Zusammenfassungen und Analysen basierend auf den Wissenspunkten im Internet erstellt (ich bin). (nur ein Träger des Internets) (Arbeiter, nicht Schöpfer)), wenn es Fehler oder Auslassungen gibt, kritisieren Sie mich bitte heftig zu diesem Thema.

Dieses Thema richtet sich derzeit an Browser und node.js wurde nicht berücksichtigt. Allerdings handelt es sich bei allen um JavaScript Es6-Syntax, die mehr oder weniger gleich ist.

Wann löst JavaScript einen Fehler aus?

Im Allgemeinen in zwei Situationen unterteilt:

In JavaScript integrierte Fehler

Von Entwicklern aktiv ausgelöste Fehler

JavaScript-Engine Wird automatisch ausgelöst Fehler

Die Fehler, auf die wir in den meisten Szenarien stoßen, sind diese Art von Fehlern. Wenn ein Javascript-Syntaxfehler, Code-Referenzfehler, Typfehler usw. auftritt, löst die JavaScript-Engine automatisch solche Fehler aus. Einige Szenarien sind wie folgt:

Szenario 1

console.log(a.notExited)
// 浏览器会抛出 Uncaught ReferenceError: a is not defined

Szenario 2

const a;
// 浏览器抛出 Uncaught SyntaxError: Missing initializer in const declaration

Syntaxfehler gibt der Browser normalerweise so schnell wie möglich aus und wartet nicht bis zur Ausführung. Melden Sie einen Fehler.

Szenario 3

let data;
data.forEach(v=>{})
// Uncaught TypeError: Cannot read property 'forEach' of undefined

Manuell ausgelöste Fehler

sind im Allgemeinen benutzerdefinierte Fehlerausnahmen, die von Klassenbibliotheken entwickelt wurden (z. B. das Auslösen illegaler Fehlerausnahmen wie Parameter). Oder ändern Sie die Fehlermeldung erneut und melden Sie sie, um das Verständnis zu erleichtern.

Szenario 1

function sum(a,b){
  if(typeof a !== 'number') {
    throw TypeError('Expected a to be a number.');
  }
  if(typeof b !== 'number') {
    throw TypeError('Expected b to be a number.');
  }
  return a + b;
}
sum(3,'d');
// 浏览器抛出 uncaught TypeError: Expected b to be a number.

Szenario 2

Natürlich müssen wir das nicht unbedingt tun.

let data;

try {
  data.forEach(v => {});
} catch (error) {
  error.message = 'data 没有定义,data 必须是数组。';
  error.name = 'DataTypeError';
  throw error;
}

Wie erstelle ich ein Fehlerobjekt?

Die Erstellungssyntax lautet wie folgt:

new Error([message[,fileName,lineNumber]])

Das Gleiche gilt für das Weglassen der neuen Syntax.

Der Dateiname und die Zeilennummer sind nicht mit allen Browsern kompatibel und Google unterstützt sie nicht, sodass sie ignoriert werden können.

Der Error-Konstruktor ist ein allgemeiner Fehlertyp. Neben dem Error-Typ gibt es auch TypeError, RangeError und andere Typen.

Fehlerinstanz

Alle hier aufgeführten Eigenschaften und Methoden der Prototypenkette werden nicht erklärt. Die Vererbungseigenschaften und der Komfort der tieferen Prototypenkette werden nicht erläutert. Einige kompatible und ungewöhnliche Eigenschaften und Methoden werden nicht erklärt.

console.log(Error.prototype)
// 浏览器输出 {constructor: ƒ, name: "Error", message: "", toString: ƒ}

Andere Fehlertypkonstruktoren erben Error und die Instanzen sind konsistent.

Attribute

Error.prototype.message

Fehlermeldung, Error("msg").message === "msg".

Error.prototype.name

Fehlertyp (Name), Error("msg").name === "Fehler". Bei TypeError lautet der Name TypeError.

Error.prototype.stack

Das Error-Objekt stellt eine Funktionsverfolgungsmethode als nicht standardmäßiges Stack-Attribut bereit. Egal aus welchem ​​Modus die Funktion aufgerufen wird, aus welcher Zeile oder Datei und mit welchen Parametern. Dieser Stapel wird aus dem frühesten aktuellen Aufruf generiert und gibt den ursprünglichen Aufruf des globalen Bereichs zurück.

Dies ist keine Spezifikation, es besteht Kompatibilität. Nach dem Test unterstützen Google, Firefox, Edge und Safari alle diese Funktion (alle getestet unter der neuesten Version 2019-04-02), der IE unterstützt sie jedoch nicht.

Methode

Error.prototype.constructor

Error.prototype.toString

Das Rückgabewertformat ist ${name} : ${Nachricht}.

Häufig verwendete Fehlertypen

Zusätzlich zum allgemeinen Fehlerkonstruktor verfügt JavaScript auch über fünf häufig verwendete andere Arten von Fehlerkonstruktoren.

TypeError

Erstellt eine Fehlerinstanz, die die Fehlerursache angibt: Die Variable oder der Parameter gehört keinem gültigen Typ an.

throw TypeError("类型错误");
// Uncaught TypeError: 类型错误

RangeError

Erstellt eine Fehlerinstanz, die die Fehlerursache angibt: Eine numerische Variable oder ein Parameter überschreitet ihren gültigen Bereich.

throw RangeError("数值超出有效范围");
// Uncaught RangeError: 数值超出有效范围

ReferenceError

Erstellt eine Fehlerinstanz, die die Fehlerursache angibt: ungültige Referenz.

throw ReferenceError("无效引用");
// Uncaught ReferenceError: 无效引用

SyntaxError

Erstellt eine Fehlerinstanz, die die Fehlerursache angibt: Syntaxfehler. Dieses Szenario wird selten verwendet, es sei denn, die Klassenbibliothek definiert eine neue Syntax (z. B. eine Vorlagensyntax).

throw SyntaxError("语法错误");
// Uncaught SyntaxError: 语法错误

URIError

Erstellen Sie eine Fehlerinstanz, um die Fehlerursache anzugeben: URI-bezogene Fehler.

throw URIError("url 不合法");
// Uncaught RangeError: url 不合法

自定义 Error 类型

自定义新的 Error 类型需要继承 Error ,如下自定义 CustomError:

function CustomError(...args){
  class InnerCustomError extends Error {
    name = "CustomError";
  }
  return new InnerCustomError(...args);
}

继承 Error 后,我们只需要对 name 做重写,然后封装成可直接调用的函数即可。

如何拦截 JavaScript 错误?

既然没人能保证 web 应用不会出现 bug,那么出现异常报错时,如何拦截并进行一些操作呢?

try…catch… 拦截

这是拦截 JavaScript 错误,拦截后,如果不手动抛出错误,这个错误将静默处理。平常写代码如果我们知道某段代码可能会出现报错问题,就可以使用这种方式。如下:

const { data } = this.props;
try {
  data.forEach(d=>{});
  // 如果 data 不是数组就会报错
} catch(err){
  console.error(err);
  // 这里可以做上报处理等操作
}

一些使用方式

十分不友好的处理方式

try...catch... 使用需要注意,try…catch… 后,错误会被拦截,如果不主动抛出错误,那么无法知道报错位置。如下面的处理方式就是不好的。

function badHandler(fn) {
  try {
    return fn();
  } catch (err) { /**noop,不做任何处理**/ }
  return null;
}
badHandler();

这样 fn 回调发送错误后,我们无法知道错误是在哪里发生的,因为已经被 try…catch 了,那么如何解决这个问题呢?

相对友好但糟糕的处理方式
function CustomError(...args){
  class InnerCustomError extends Error {
    name = "CustomError";
  }
  return new InnerCustomError(...args);
}
function uglyHandlerImproved(fn) {
  try {
    return fn();
  } catch (err) { 
    throw new CustomError(err.message);
  }
  return null;
}
badHandler();

现在,这个自定义的错误对象包含了原本错误的信息,因此变得更加有用。但是因为再度抛出来,依然是未处理的错误。

try…catch… 可以拦截异步错误吗?

这个也要分场景,也看个人的理解方向,首先理解下面这句话:

try…catch 只会拦截当前执行环境的错误,try 块中的异步已经脱离了当前的执行环境,所以 try…catch… 无效。

setTimeout 和 Promise 都无法通过 try…catch 捕获到错误,指的是 try 包含异步(非当前执行环境),不是异步包含 try(当前执行环境)。异步无效和有效 try…catch 如下:

setTimeout

这个无效:

try {
  setTimeout(() => {
    data.forEach(d => {});
  });
} catch (err) {
  console.log('这里不会运行');
}

下面的 try…catch 才会有效:

setTimeout(() => {
  try {
    data.forEach(d => {});
  } catch (err) {
    console.log('这里会运行');
  }
});

Promise

这个无效:

try {
  new Promise(resolve => {
    data.forEach(d => {});
    resolve();
  });
} catch (err) {
  console.log('这里不会运行');
}

下面的 try…catch 才会有效:

new Promise(resolve => {
  try {
    data.forEach(d => {});
  } catch (err) {
    console.log('这里会运行');
  }
});

小结

不是所有场景都需要 try…catch… 的,如果所有需要的地方都 try…catch,那么代码将变得臃肿,可读性变差,开发效率变低。那么我需要统一获取错误信息呢?有没有更好的处理方式?当然有,后续会提到。

Promise 错误拦截

Promise.prototype.catch 可以达到 try…catch 一样的效果,只要是在 Promise 相关的处理中报错,都会被 catch 到。当然如果你在相关回调函数中 try…catch,然后做了静默提示,那么也是 catch  不到的。

如下会被 catch 到:

new Promise(resolve => {
  data.forEach(v => {});
}).catch(err=>{/*这里会运行*/})

下面的不会被 catch 到:

new Promise(resolve => {
  try {
      data.forEach(v => {});
  }catch(err){}
}).catch(err=>{/*这里不会运行*/})

Promise 错误拦截,这里就不详细说了,如果你看懂了 try…catch,这个也很好理解。

setTimeout 等其他异步错误拦截呢?

目前没有相关的方式直接拦截 setTimeout 等其他异步操作。

如果要拦截 setTimeout 等异步错误,我们需要在异步回调代码中处理,如:

setTimeout(() => {
  try {
    data.forEach(d => {});
  } catch (err) {
    console.log('这里会运行');
  }
});

这样可以拦截到 setTimeout 回调发生的错误,但是如果是下面这样 try…catch 是无效的:

try {
  setTimeout(() => {
    data.forEach(d => {});
  });
} catch (err) {
  console.log('这里不会运行');
}

如何获取 JavaScript 错误信息?

你可以使用上面拦截错误信息的方式获取到错误信息。但是呢,你要每个场景都要去拦截一遍吗?首先我们不确定什么地方会发生错误,然后我们也不可能每个地方都去拦截错误。

不用担心,JavaScript 也考虑到了这一点,提供了一些便捷的获取方式(不是拦截,错误还是会终止程序的运行,除非主动拦截了)。

window.onerror 事件获取错误信息

onerror 事件无论是异步还是非异步错误(除了 Promise 错误),onerror 都能捕获到运行时错误。

需要注意一下几点:

window.onerror 函数只有在返回 true 的时候,异常才不会向上抛出,否则即使是知道异常的发生控制台还是会显示 Uncaught Error: xxxxx。如果使用 addEventListener,event.preventDefault() 可以达到同样的效果。

window.onerror 是无法捕获到网络异常的错误、