Le seul objet d'erreur de champ standard est la message
propriété. (Voir MDN ou Spécification du langage EcmaScript, section 15.11) Tout le reste est spécifique à la plate-forme.
Les environnements de mâts définissent la stack
propriété, mais fileName
etlineNumber
sont pratiquement inutiles à utiliser dans l' héritage.
Ainsi, l'approche minimaliste est:
function MyError(message) {
this.name = 'MyError';
this.message = message;
this.stack = (new Error()).stack;
}
MyError.prototype = new Error; // <-- remove this if you do not
// want MyError to be instanceof Error
Vous pouvez renifler la pile, en décaler les éléments indésirables et en extraire des informations telles que fileName et lineNumber, mais cela nécessite des informations sur la plateforme sur laquelle JavaScript est actuellement exécuté. La plupart des cas, ce n'est pas nécessaire - et vous pouvez le faire en post-mortem si vous le voulez vraiment.
Safari est une exception notable. Il n'y a pas de stack
propriété, mais le throw
mot clé définit sourceURL
etline
propriétés de l'objet qui est lancé. Ces choses sont garanties d'être correctes.
Les cas de test que j'ai utilisés peuvent être trouvés ici: JavaScript self-made Error comparaison d'objets .
this.name = 'MyError'
extérieur de la fonction et la modifierMyError.prototype.name = 'MyError'
.function MyError(message) { this.message = message; this.stack = Error().stack; } MyError.prototype = Object.create(Error.prototype); MyError.prototype.name = "MyError";
MyError.prototype.constructor = MyError
aussi.this
, non?Dans ES6:
la source
la source
var supportsClasses = false; try {eval('class X{}'); supportsClasses = true;} catch (e) {}
this.name = this.constructor.name;
plutôt.En bref:
Si vous utilisez ES6 sans transpilers :
Si vous utilisez le transpilateur Babel :
Option 1: utilisez babel-plugin-transform-builtin-extend
Option 2: faites-le vous-même (inspiré de cette même bibliothèque)
Si vous utilisez pure ES5 :
Alternative: utiliser un cadre classtrophobe
Explication:
Pourquoi étendre la classe Error en utilisant ES6 et Babel est un problème?
Parce qu'une instance de CustomError n'est plus reconnue comme telle.
En fait, de la documentation officielle de Babel, vous ne pouvez pas étendre tout JavaScript intégré dans les classes telles que
Date
,Array
,DOM
ouError
.Le problème est décrit ici:
Qu'en est-il des autres réponses SO?
Toutes les réponses données corrigent le
instanceof
problème mais vous perdez l'erreur régulièreconsole.log
:Alors qu'en utilisant la méthode mentionnée ci-dessus, non seulement vous corrigez le
instanceof
problème, mais vous conservez également l'erreur régulièreconsole.log
:la source
class CustomError extends Error { /* ... */}
ne gère pas correctement les arguments spécifiques au fournisseur (lineNumber
, etc.), «Extension d'erreur en Javascript avec la syntaxe ES6» est spécifique à Babel, votre solution ES5 utiliseconst
et ne gère pas les arguments personnalisés.console.log(new CustomError('test') instanceof CustomError);// false
était vrai au moment de la rédaction du présent rapport, mais qu'il a maintenant été résolu. En fait, le problème lié dans la réponse a été résolu et nous pouvons tester le comportement correct ici et en collant le code dans le REPL et en voyant comment il est correctement transpilé pour instancier avec la chaîne de prototype correcte.Edit: Veuillez lire les commentaires. Il s'avère que cela ne fonctionne que bien dans V8 (Chrome / Node.JS) Mon intention était de fournir une solution multi-navigateur, qui fonctionnerait dans tous les navigateurs, et fournirait une trace de pile là où le support est là.
Modifier: j'ai créé ce wiki communautaire pour permettre plus de modifications.
La solution pour V8 (Chrome / Node.JS), fonctionne dans Firefox et peut être modifiée pour fonctionner principalement correctement dans IE. (voir fin du post)
Message original sur "Montrez-moi le code!"
Version courte:
Je garde
this.constructor.prototype.__proto__ = Error.prototype
à l'intérieur de la fonction pour garder tout le code ensemble. Mais vous pouvez également remplacerthis.constructor
parUserError
et cela vous permet de déplacer le code vers l'extérieur de la fonction, de sorte qu'il n'est appelé qu'une seule fois.Si vous suivez cette route, assurez-vous d'appeler cette ligne avant la première fois que vous lancez
UserError
.Cette mise en garde n'applique pas la fonction, car les fonctions sont créées en premier, quel que soit l'ordre. Ainsi, vous pouvez déplacer la fonction à la fin du fichier, sans problème.
Compatibilité du navigateur
Fonctionne dans Firefox et Chrome (et Node.JS) et remplit toutes les promesses.
Internet Explorer échoue dans les cas suivants
Les erreurs ne doivent
err.stack
pas commencer, donc "ce n'est pas ma faute".Error.captureStackTrace(this, this.constructor)
n'existe pas, vous devez donc faire autre chose commetoString
cesse d'exister lorsque vous sous-classeError
. Vous devez donc également ajouter.IE ne sera pas considéré
UserError
comme uninstanceof Error
sauf si vous exécutez la commande suivante un certain temps avantthrow UserError
la source
Error.call(this)
ne fait en effet rien car il retourne une erreur plutôt que de la modifierthis
.UserError.prototype = Error.prototype
est trompeur. Cela ne fait pas d'héritage, cela fait d'eux la même classe .Object.setPrototypeOf(this.constructor.prototype, Error.prototype)
c'est préférablethis.constructor.prototype.__proto__ = Error.prototype
, du moins pour les navigateurs actuels.Pour éviter le passe- partout pour chaque type d'erreur, j'ai combiné la sagesse de certaines des solutions en une
createErrorType
fonction:Vous pouvez ensuite définir facilement de nouveaux types d'erreur comme suit:
la source
this.name = name;
?name
c'est déjà installé sur le prototype, ce n'est plus nécessaire. Je l'ai retiré. Merci!En 2018 , je pense que c'est la meilleure façon; qui prend en charge IE9 + et les navigateurs modernes.
MISE À JOUR : Voir ce test et dépôt pour une comparaison sur différentes implémentations.
Méfiez-vous également que la
__proto__
propriété est obsolète, ce qui est largement utilisé dans d'autres réponses.la source
setPrototypeOf()
? Au moins selon MDN, il est généralement déconseillé de l'utiliser si vous pouvez accomplir la même chose en définissant simplement la.prototype
propriété sur le constructeur (comme vous le faites dans leelse
bloc pour les navigations qui n'en ont passetPrototypeOf
).setPrototypeOf
. Mais si vous en avez toujours besoin (comme le demande OP), vous devez utiliser la méthodologie intégrée. Comme MDN l'indique, cela est considéré comme la bonne façon de définir le prototype d'un objet. En d'autres termes, MDN dit de ne pas modifier le prototype (car cela affecte les performances et l'optimisation) mais si vous devez, utilisezsetPrototypeOf
.CustomError.prototype = Object.create(Error.prototype)
). Définit égalementObject.setPrototypeOf(CustomError, Error.prototype)
le prototype du constructeur lui-même plutôt que de spécifier le prototype pour de nouvelles instances deCustomError
. Quoi qu'il en soit, en 2016, je pense qu'il existe en fait une meilleure façon d'étendre les erreurs, même si je suis toujours en train de trouver comment l'utiliser avec Babel: github.com/loganfsmyth/babel-plugin-transform-builtin-extend/…CustomError.prototype = Object.create(Error.prototype)
modifie également le prototype. Vous devez le changer car il n'y a pas de logique d'extension / héritage intégrée dans ES5. Je suis sûr que le plugin babel que vous mentionnez fait des choses similaires.Object.setPrototypeOf
n'a pas de sens ici, du moins pas de la façon dont vous l'utilisez: gist.github.com/mbrowne/4af54767dcb3d529648f5a8aa11d6348 . Peut-être que vous vouliez écrireObject.setPrototypeOf(CustomError.prototype, Error.prototype)
- cela aurait un peu plus de sens (bien qu'il n'apporte toujours aucun avantage par rapport au simple réglageCustomError.prototype
).Par souci d'exhaustivité - simplement parce qu'aucune des réponses précédentes n'a mentionné cette méthode - si vous travaillez avec Node.js et n'avez pas à vous soucier de la compatibilité du navigateur, l'effet souhaité est assez facile à obtenir avec le intégré
inherits
duutil
module ( documents officiels ici ).Par exemple, supposons que vous souhaitiez créer une classe d'erreur personnalisée qui prend un code d'erreur comme premier argument et le message d'erreur comme deuxième argument:
fichier custom-error.js :
Vous pouvez maintenant instancier et passer / lancer votre
CustomError
:Notez qu'avec cet extrait, la trace de la pile aura le nom et la ligne de fichier corrects et l'instance d'erreur aura le nom correct!
Cela se produit en raison de l'utilisation de la
captureStackTrace
méthode, qui crée unestack
propriété sur l'objet cible (dans ce cas, l'CustomError
instanciation). Pour plus de détails sur son fonctionnement, consultez la documentation ici .la source
this.message = this.message;
est-ce mal ou y a-t-il encore des choses folles que je ne connais pas sur JS?La réponse votée par Crescent Fresh est trompeuse. Bien que ses avertissements ne soient pas valides, il existe d'autres limitations qu'il ne traite pas.
Premièrement, le raisonnement du paragraphe "Mises en garde:" de Crescent n'a pas de sens. L'explication implique que coder "un tas de if (erreur instanceof MyError) d'autre ..." est en quelque sorte fastidieux ou verbeux par rapport à plusieurs déclarations catch. Les instructions instanceof multiples dans un seul bloc catch sont tout aussi concises que les instructions catch multiples - du code propre et concis sans astuces. C'est un excellent moyen d'émuler la grande gestion des erreurs spécifiques au sous-type jetable de Java.
WRT "apparaît que la propriété de message de la sous-classe n'est pas définie", ce n'est pas le cas si vous utilisez une sous-classe Error correctement construite. Pour créer votre propre sous-classe ErrorX Error, copiez simplement le bloc de code commençant par "var MyError =", en remplaçant le mot "MyError" par "ErrorX". (Si vous souhaitez ajouter des méthodes personnalisées à votre sous-classe, suivez l'exemple de texte).
La limitation réelle et significative de la sous-classification des erreurs JavaScript est que pour les implémentations ou les débogueurs JavaScript qui suivent et signalent la trace de la pile et l'emplacement de l'instanciation, comme FireFox, un emplacement dans votre propre implémentation de la sous-classe Error sera enregistré comme point d'instanciation de la alors que si vous utilisiez une erreur directe, ce serait l'emplacement où vous avez exécuté "nouvelle erreur (...)"). Les utilisateurs d'IE ne le remarqueraient probablement jamais, mais les utilisateurs de Fire Bug sur FF verront les valeurs de nom de fichier et de numéro de ligne inutiles rapportées à côté de ces erreurs, et devront explorer la trace de la pile jusqu'à l'élément # 1 pour trouver l'emplacement réel d'instanciation.
la source
Crescent Fresh's
a été supprimée!Et cette solution?
Au lieu de lancer votre erreur personnalisée en utilisant:
Vous emballeriez l'objet Error (un peu comme un décorateur):
Cela garantit que tous les attributs sont corrects, tels que la pile, fileName lineNumber, et cetera.
Il vous suffit alors de copier les attributs ou de définir des getters pour eux. Voici un exemple d'utilisation de getters (IE9):
la source
new MyErr (arg1, arg2, new Error())
et dans le constructeur MyErr, nous utilisonsObject.assign
pour assigner les propriétés du dernier arg àthis
Comme certains l'ont dit, c'est assez facile avec ES6:
J'ai donc essayé cela dans mon application (Angular, Typescript) et cela n'a tout simplement pas fonctionné. Après un certain temps, j'ai constaté que le problème venait de Typescript: O
Voir https://github.com/Microsoft/TypeScript/issues/13965
C'est très dérangeant car si vous le faites:
Dans le nœud ou directement dans votre navigateur, il affichera:
Custom error
Essayez de l'exécuter avec Typescript dans votre projet sur le terrain de jeu Typescript, il s'affichera
Basic error
...La solution consiste à procéder comme suit:
la source
Ma solution est plus simple que les autres réponses fournies et n'a pas les inconvénients.
Il préserve la chaîne de prototypes Error et toutes les propriétés sur Error sans avoir besoin de connaissances spécifiques à leur sujet. Il a été testé dans Chrome, Firefox, Node et IE11.
La seule limitation est une entrée supplémentaire en haut de la pile d'appels. Mais cela est facilement ignoré.
Voici un exemple avec deux paramètres personnalisés:
Exemple d'utilisation:
Pour les environnements qui nécessitent un polyfil de setPrototypeOf:
la source
throw CustomError('err')
au lieu dethrow new CustomError('err')
Dans l'exemple ci-dessus
Error.apply
(aussiError.call
) ne fait rien pour moi (Firefox 3.6 / Chrome 5). Une solution de contournement que j'utilise est:la source
Dans Node, comme d'autres l'ont dit, c'est simple:
la source
Je veux juste ajouter à ce que d'autres ont déjà déclaré:
Pour vous assurer que la classe d'erreur personnalisée s'affiche correctement dans la trace de la pile, vous devez définir la propriété de nom du prototype de la classe d'erreur personnalisée sur la propriété de nom de la classe d'erreur personnalisée. Voici ce que je veux dire:
Ainsi, l'exemple complet serait:
Quand tout est dit et fait, vous lancez votre nouvelle exception et cela ressemble à ceci (j'ai paresseusement essayé cela dans les outils de développement de chrome):
la source
Mes 2 cents:
Pourquoi une autre réponse?
a) Parce que l'accès à la
Error.stack
propriété (comme dans certaines réponses) a une grande pénalité de performance.b) Parce que ce n'est qu'une seule ligne.
c) Parce que la solution sur https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error ne semble pas conserver les informations de la pile.
exemple d'utilisation
http://jsfiddle.net/luciotato/xXyeB/
Qu'est ce que ça fait?
this.__proto__.__proto__
estMyError.prototype.__proto__
, il définit donc__proto__
FOR ALL INSTANCES of MyError sur une erreur spécifique nouvellement créée. Il conserve les propriétés et méthodes de la classe MyError et place également les nouvelles propriétés Error (y compris .stack) dans la__proto__
chaîne.Problème évident:
Vous ne pouvez pas avoir plus d'une instance de MyError avec des informations utiles sur la pile.
N'utilisez pas cette solution si vous ne comprenez pas bien ce qui se
this.__proto__.__proto__=
passe.la source
Étant donné que les exceptions JavaScript sont difficiles à sous-classer, je ne sous-classe pas. Je viens de créer une nouvelle classe d'exception et j'utilise une erreur à l'intérieur de celle-ci. Je modifie la propriété Error.name pour qu'elle ressemble à mon exception personnalisée sur la console:
La nouvelle exception ci-dessus peut être levée comme une erreur régulière et fonctionnera comme prévu, par exemple:
Attention: la trace de la pile n'est pas parfaite, car elle vous amènera à l'endroit où la nouvelle erreur est créée et non à l'endroit où vous jetez. Ce n'est pas un gros problème sur Chrome, car il vous fournit une trace de pile complète directement dans la console. Mais c'est plus problématique sur Firefox, par exemple.
la source
m = new InvalidInputError(); dontThrowMeYet(m);
m = new ...
alorsPromise.reject(m)
. Ce n'est pas une nécessité, mais le code est plus facile à lire.Comme indiqué dans la réponse de Mohsen, dans ES6, il est possible d'étendre les erreurs en utilisant des classes. C'est beaucoup plus facile et leur comportement est plus cohérent avec les erreurs natives ... mais malheureusement, ce n'est pas une question simple à utiliser dans le navigateur si vous devez prendre en charge les navigateurs pré-ES6. Voir ci-dessous pour quelques notes sur la façon dont cela pourrait être mis en œuvre, mais en attendant, je suggère une approche relativement simple qui incorpore certaines des meilleures suggestions d'autres réponses:
Dans ES6, c'est aussi simple que:
... et vous pouvez détecter la prise en charge des classes ES6 avec
try {eval('class X{}')
, mais vous obtiendrez une erreur de syntaxe si vous essayez d'inclure la version ES6 dans un script chargé par des navigateurs plus anciens. Par conséquent, la seule façon de prendre en charge tous les navigateurs serait de charger dynamiquement un script distinct (par exemple via AJAX oueval()
) pour les navigateurs prenant en charge ES6. Une autre complication est que ceeval()
n'est pas pris en charge dans tous les environnements (en raison des politiques de sécurité du contenu), ce qui peut ou non être une considération pour votre projet.Donc, pour l'instant, soit la première approche ci-dessus, soit simplement en utilisant
Error
directement sans essayer de l'étendre, cela semble être le meilleur qui puisse être fait pour le code qui doit prendre en charge les navigateurs non ES6.Il existe une autre approche que certaines personnes voudront peut-être envisager, qui consiste à utiliser, le
Object.setPrototypeOf()
cas échéant, pour créer un objet d'erreur qui est une instance de votre type d'erreur personnalisé, mais qui ressemble et se comporte davantage comme une erreur native dans la console (grâce à la réponse de Ben pour la recommandation). Voici mon point de vue sur cette approche: https://gist.github.com/mbrowne/fe45db61cea7858d11be933a998926a8 . Mais étant donné qu'un jour nous pourrons simplement utiliser ES6, personnellement, je ne suis pas sûr que la complexité de cette approche en vaille la peine.la source
La façon de faire ce droit est de renvoyer le résultat de la demande du constructeur, ainsi que de définir le prototype de la manière habituelle javascript compliquée:
Les seuls problèmes avec cette façon de faire à ce stade (je l'ai un peu répété) sont que
stack
etmessage
ne sont pas incluses dansMyError
etLe premier problème pourrait être résolu en parcourant toutes les propriétés d'erreur non énumérables en utilisant l'astuce dans cette réponse: est-il possible d'obtenir les noms de propriété hérités non énumérables d'un objet? , mais ceci n'est pas supporté par ie <9. Le deuxième problème pourrait être résolu en supprimant cette ligne dans la trace de la pile, mais je ne sais pas comment le faire en toute sécurité (peut-être simplement en supprimant la deuxième ligne de e.stack.toString () ??).
la source
L'extrait montre tout.
la source
Je prendrais un peu de recul et je me demanderais pourquoi vous voulez faire ça? Je pense qu'il s'agit de traiter différemment différentes erreurs.
Par exemple, en Python, vous pouvez restreindre l'instruction catch à catch uniquement
MyValidationError
, et peut-être voulez-vous pouvoir faire quelque chose de similaire en javascript.Vous ne pouvez pas faire cela en javascript. Il n'y aura qu'un seul bloc de capture. Vous êtes censé utiliser une instruction if sur l'erreur pour déterminer son type.
catch(e) { if(isMyValidationError(e)) { ... } else { // maybe rethrow? throw e; } }
Je pense que je préfère lancer un objet brut avec un type, un message et toutes les autres propriétés que vous jugez appropriées.
Et quand vous attrapez l'erreur:
la source
error.stack
, outillage standard ne fonctionnera pas avec elle, etc. , etc. Une meilleure façon serait d'ajouter des propriétés à une instance d'erreur, par exemplevar e = new Error(); e.type = "validation"; ...
Décorateur d'erreur personnalisé
Ceci est basé sur la réponse de George Bailey , mais étend et simplifie l'idée originale. Il est écrit en CoffeeScript, mais est facile à convertir en JavaScript. L'idée est d'étendre l'erreur personnalisée de Bailey avec un décorateur qui l'enveloppe, vous permettant de créer facilement de nouvelles erreurs personnalisées.
Remarque: cela ne fonctionnera qu'en V8. Il n'y a pas de prise
Error.captureStackTrace
en charge dans d'autres environnements.Définir
Le décorateur prend un nom pour le type d'erreur, et renvoie une fonction qui prend un message d'erreur et entoure le nom de l'erreur.
Utilisation
Il est désormais simple de créer de nouveaux types d'erreur.
Pour le plaisir, vous pouvez maintenant définir une fonction qui lance un
SignatureError
si elle est appelée avec trop d'arguments.Cela a été assez bien testé et semble fonctionner parfaitement sur V8, en conservant le traçage, la position, etc.
Remarque: L'utilisation
new
est facultative lors de la construction d'une erreur personnalisée.la source
si vous ne vous souciez pas des performances pour les erreurs, c'est le plus petit que vous puissiez faire
vous pouvez l'utiliser sans nouveau juste MyError (message)
En changeant le prototype après l'appel de l'erreur constructeur, nous n'avons pas à définir la pile d'appels et le message
la source
Mohsen a une excellente réponse ci-dessus dans ES6 qui définit le nom, mais si vous utilisez TypeScript ou si vous vivez dans le futur où, espérons-le, cette proposition de champs de classe publics et privés a dépassé l'étape 3 en tant que proposition et l'a faite dans l'étape 4 dans le cadre d'ECMAScript / JavaScript, alors vous voudrez peut-être savoir que cela est alors un peu plus court. L'étape 3 est où les navigateurs commencent à implémenter des fonctionnalités, donc si votre navigateur le prend en charge, le code ci-dessous pourrait bien fonctionner. (Testé dans le nouveau navigateur Edge v81, il semble fonctionner correctement). Soyez averti bien que cette fonctionnalité soit instable pour le moment et qu'elle doive être utilisée avec prudence et vous devriez toujours vérifier la prise en charge du navigateur sur les fonctionnalités instables. Ce message est principalement destiné aux futurs habitants lorsque les navigateurs pourraient le prendre en charge. Pour vérifier la vérification de l'assistance MDNetPuis-je utiliser . Il est actuellement pris en charge à 66% sur le marché des navigateurs, mais ce n'est pas génial, donc si vous voulez vraiment l'utiliser maintenant et ne voulez pas attendre, utilisez un transpilateur comme Babel ou quelque chose comme TypeScript .
Comparez cela à une erreur sans nom qui, une fois lancée, n'enregistrera pas son nom.
la source