Maison  >  Article  >  interface Web  >  Analyse des exceptions d'erreur en JavaScript (avec exemples)

Analyse des exceptions d'erreur en JavaScript (avec exemples)

不言
不言avant
2019-04-04 11:22:422509parcourir

Le contenu de cet article concerne l'analyse des erreurs et des exceptions en JavaScript (avec des exemples). Il a une certaine valeur de référence. J'espère qu'il sera utile. vous. aider.

Mon conseil est de ne pas cacher vos erreurs, d'être assez courageux pour les rejeter. Personne ne devrait avoir honte si un bug dans le code provoque le crash du programme. Nous pouvons interrompre le programme et laisser l'utilisateur recommencer. Les erreurs sont inévitables, ce qui compte, c'est la façon dont vous les gérez.

JavaScript fournit un mécanisme de gestion des erreurs. Les erreurs sont des accidents anormaux qui interfèrent avec le déroulement normal du programme. Et personne ne peut maintenir le programme sans bug. Alors, si vous rencontrez un bug spécial après une connexion en ligne, comment pouvez-vous localiser le problème plus rapidement ? C’est ce dont notre sujet doit discuter.

Ce qui suit décrira les connaissances de base des erreurs JavaScript, comment intercepter et capturer les exceptions et comment signaler facilement les erreurs en ligne. J'ai également fait un résumé et une analyse basés sur les points de connaissance sur Internet (je le suis. juste un porteur d'Internet) (travailleur, pas créateur)), s'il y a des erreurs ou des omissions, merci de me critiquer sévèrement sur la question.

Ce sujet s'adresse actuellement aux navigateurs, et node.js n'a pas été pris en compte. Cependant, ce sont tous de la syntaxe JavaScript Es6, qui est plus ou moins la même.

Quand JavaScript génère-t-il une erreur ?

Généralement divisé en deux situations :

Erreurs intégrées JavaScript

Erreurs activement générées par les développeurs

Moteur JavaScript automatiquement généré erreurs

Les erreurs que nous rencontrons dans la plupart des scénarios sont ce type d'erreurs. Si une erreur de syntaxe Javscript, une erreur de référence de code, une erreur de type, etc. se produit, le moteur JavaScript déclenchera automatiquement ces erreurs. Certains scénarios sont les suivants :

Scénario 1

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

Scénario 2

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

Erreur de syntaxe génère généralement une erreur immédiatement et n'attend pas l'exécution. L'erreur sera signalée lorsque .

Scénario 3

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

Les erreurs générées manuellement

sont généralement des exceptions d'erreur personnalisées développées par des bibliothèques de classes (telles que des paramètres et d'autres exceptions d'erreur illégales levées). Ou modifiez à nouveau le message d'erreur et signalez-le pour faciliter la compréhension.

Scénario 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.

Scénario 2

Bien sûr, nous n'avons pas nécessairement besoin de faire cela.

let data;

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

Comment créer un objet Erreur ?

La syntaxe de création est la suivante :

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

Il en va de même pour l'omission de la nouvelle syntaxe.

Le fileName et le lineNumber ne sont pas compatibles avec tous les navigateurs et Google ne les prend pas en charge, ils peuvent donc être ignorés.

Le constructeur Error est un type d'erreur général. En plus du type Error, il existe également TypeError, RangeError et d'autres types.

Instance d'erreur

Toutes les propriétés et méthodes de la chaîne de prototypes répertoriées ici ne sont pas expliquées. Certaines propriétés et méthodes compatibles et peu courantes ne sont pas expliquées.

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

D'autres constructeurs de types d'erreur héritent de Error et les instances sont cohérentes.

Attributs

Error.prototype.message

Message d'erreur, Error("msg").message === "msg".

Error.prototype.name

Type d'erreur (nom), Error("msg").name === "Erreur". Si TypeError, alors le nom est TypeError.

Error.prototype.stack

L'objet Error fournit une méthode de traçage de fonction en tant qu'attribut de pile non standard. Quel que soit le mode à partir duquel cette fonction est appelée, à partir de quelle ligne ou fichier et avec quels paramètres. Cette pile est générée à partir du premier appel récent, renvoyant l'appel de portée globale d'origine.

Ceci n'est pas une spécification, il y a une compatibilité. Après tests, Google, Firefox, Edge et Safari prennent tous en charge cette fonctionnalité (tous testés sous la dernière version 2019-04-02), mais IE ne la prend pas en charge.

Méthode

Error.prototype.constructor

Error.prototype.toString

Le format de la valeur de retour est ${name} : ${message}.

Types d'erreur couramment utilisés

En plus du constructeur d'erreur général, JavaScript possède également 5 autres types courants de constructeurs d'erreur.

TypeError

Crée une instance d'Erreur indiquant la cause de l'erreur : la variable ou le paramètre n'appartient pas à un type valide.

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

RangeError

Créez une instance d'Erreur pour indiquer la cause de l'erreur : une variable ou un paramètre numérique dépasse sa plage valide.

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

ReferenceError

Crée une instance d'Erreur, indiquant la cause de l'erreur : référence invalide.

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

SyntaxError

Crée une instance d'Erreur, indiquant la cause de l'erreur : erreur de syntaxe. Ce scénario est rarement utilisé, sauf si la bibliothèque de classes définit une nouvelle syntaxe (telle qu'une syntaxe de modèle).

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

URIError

Créez une instance d'Erreur pour indiquer la cause de l'erreur : les erreurs liées à l'uri.

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 是无法捕获到网络异常的错误、