Je veux lancer certaines choses dans mon code JS et je veux qu'elles soient instanceof Error, mais je veux aussi qu'elles soient autre chose.
En Python, typiquement, on sous-classe les exceptions.
Quelle est la chose appropriée à faire en JS ?
Je veux lancer certaines choses dans mon code JS et je veux qu'elles soient instanceof Error, mais je veux aussi qu'elles soient autre chose.
En Python, typiquement, on sous-classe les exceptions.
Quelle est la chose appropriée à faire en JS ?
Le seul champ standard de l'objet Error est le champ message
la propriété. (Voir MDN ou Spécification du langage EcmaScript, section 15.11) Tout le reste est spécifique à la plate-forme.
La plupart des environnements définissent le stack
propriété, mais fileName
y lineNumber
sont pratiquement inutiles pour être utilisés en héritage.
Donc, 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 pourriez renifler la pile, en retirer les éléments indésirables et extraire des informations telles que le nom du fichier et le numéro de ligne, mais cela nécessite des informations sur la plate-forme sur laquelle JavaScript est exécuté. Dans la plupart des cas, cela n'est pas nécessaire - et vous pouvez le faire en post-mortem si vous le souhaitez vraiment.
Safari est une exception notable. Il n'y a pas de stack
mais la propriété throw
jeux de mots-clés sourceURL
y line
de l'objet qui est lancé. Ces éléments sont garantis comme étant corrects.
Les cas de test que j'ai utilisés peuvent être trouvés ici : Comparaison d'objets d'erreur en JavaScript .
Vous pourriez déplacer le this.name = 'MyError'
en dehors de la fonction et le changer en MyError.prototype.name = 'MyError'
.
C'est la seule réponse correcte ici, bien que, pour des raisons de style, je l'écrirais probablement comme ceci. function MyError(message) { this.message = message; this.stack = Error().stack; } MyError.prototype = Object.create(Error.prototype); MyError.prototype.name = "MyError";
Exemple de stack-sniffing pour Mozilla : groups.google.com/d/topic/mozilla.dev.tech.js-engine/
Editar: Veuillez lire les commentaires. Il s'avère que cela ne fonctionne bien que dans V8 (Chrome / Node.JS) Mon intention était de fournir une solution multi-navigateurs, qui fonctionnerait dans tous les navigateurs, et de fournir une trace de pile où le soutien est là.
Editar: J'ai créé ce wiki communautaire pour permettre plus d'édition.
Solution pour V8 (Chrome / Node.JS), fonctionne dans Firefox, et peut être modifié pour fonctionner à peu près correctement dans IE (voir la fin du post).
function UserError(message) {
this.constructor.prototype.__proto__ = Error.prototype // Make this an instanceof Error.
Error.call(this) // Does not seem necessary. Perhaps remove this line?
Error.captureStackTrace(this, this.constructor) // Creates the this.stack getter
this.name = this.constructor.name; // Used to cause messages like "UserError: message" instead of the default "Error: message"
this.message = message; // Used to set the message
}
Message original sur "Show me the code !"
Version courte :
function UserError(message) {
this.constructor.prototype.__proto__ = Error.prototype
Error.captureStackTrace(this, this.constructor)
this.name = this.constructor.name
this.message = message
}
Je garde this.constructor.prototype.__proto__ = Error.prototype
à l'intérieur de la fonction pour garder tout le code ensemble. Mais vous pouvez aussi remplacer this.constructor
con UserError
et cela vous permet de déplacer le code en dehors de la fonction, afin qu'il ne soit appelé qu'une seule fois.
Si vous choisissez cette voie, assurez-vous d'appeler cette ligne. avant la première fois que vous lancez UserError
.
Cet avertissement ne s'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é des navigateurs
Fonctionne dans Firefox et Chrome (et Node.JS) et remplit toutes les promesses.
Internet Explorer échoue dans les cas suivants
Les erreurs n'ont pas err.stack
pour commencer, donc "ce n'est pas ma faute".
Error.captureStackTrace(this, this.constructor)
n'existe pas, vous devez donc faire quelque chose d'autre comme
if(Error.captureStackTrace) // AKA if not IE
Error.captureStackTrace(this, this.constructor)
toString
cesse d'exister lorsque vous sous-classez Error
. Vous devez donc également ajouter.
else
this.toString = function () { return this.name + ': ' + this.message }
IE ne prendra pas en compte UserError
pour être un instanceof Error
à moins que vous n'exécutiez le programme suivant quelque temps avant throw UserError
UserError.prototype = Error.prototype
Je ne pense pas que Firefox dispose réellement de captureStackTrace. C'est une extension V8 et elle n'est pas définie dans Firefox pour moi, et je ne peux pas trouver de références sur le web sur le fait que Firefox la supporte. (Merci quand même !)
Error.call(this)
ne fait en effet rien puisqu'il renvoie à une erreur plutôt que de modifier this
.
UserError.prototype = Error.prototype
est trompeuse. Cela ne fait pas de l'héritage, cela les rend la même classe .
La réponse de Crescent Fresh, qui a reçu de nombreux votes, est trompeuse. Bien que ses avertissements ne soient pas valables, il existe d'autres limites qu'il n'aborde pas.
Premièrement, le raisonnement du paragraphe "Caveats :" de Crescent n'a pas de sens. L'explication sous-entend que coder "un tas de if (error instanceof MyError) else ..." est en quelque sorte fastidieux ou verbeux par rapport à plusieurs instructions catch. De multiples instructions instanceof dans un seul bloc catch sont tout aussi concises que de multiples instructions catch - un code propre et concis sans aucun artifice. Il s'agit d'un excellent moyen d'émuler l'excellente gestion des erreurs spécifiques aux sous-types jetables de Java.
En ce qui concerne "la propriété 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, copiez simplement le bloc de code commençant par "var MyError =", en remplaçant le mot "MyError" par "ErrorX". (Si vous voulez ajouter des méthodes personnalisées à votre sous-classe, suivez l'exemple de texte).
La limitation réelle et significative de la sous-classe d'erreur JavaScript est que pour les implémentations JavaScript ou les débogueurs qui suivent et rapportent la trace de la pile et l'emplacement de l'instanciation, comme FireFox, un emplacement dans votre propre implémentation de sous-classe d'erreur sera enregistré comme le point d'instanciation de la classe, alors que si vous utilisez une erreur directe, ce serait l'emplacement où vous avez exécuté "new Error(...)"). Les utilisateurs d'IE ne s'en apercevront probablement jamais, mais les utilisateurs de Fire Bug sur FF verront des valeurs inutiles de nom de fichier et de numéro de ligne signalées à côté de ces Erreurs, et devront descendre dans la trace de la pile jusqu'à l'élément #1 pour trouver le véritable emplacement d'instanciation.
Ai-je bien compris que si vous ne sous-classez pas et utilisez directement new Error(...), le nom du fichier et la ligne sont signalés correctement ? Et vous dites en gros que dans la pratique (réelle et pas seulement de type sexy ou décorative) la sous-classe Errors n'a aucun sens ?
Est-ce toujours le cas ? developer.mozilla.org/fr/US/docs/Web/JavaScript/Référence/ Le numéro de ligne 2 n'est pas celui où le nouveau a été appelé
Que pensez-vous de cette solution ?
Au lieu de lancer votre erreur personnalisée en utilisant :
throw new MyError("Oops!");
Vous devez envelopper l'objet Error (un peu comme un décorateur) :
throw new MyError(Error("Oops!"));
Cette opération permet de s'assurer que tous les attributs sont corrects, tels que la pile, le nom du fichier, le numéro de ligne, etc.
Il ne vous reste plus qu'à copier les attributs ou à définir des getters pour eux. Voici un exemple utilisant les getters (IE9) :
function MyError(wrapped)
{
this.wrapped = wrapped;
this.wrapped.name = 'MyError';
}
function wrap(attr)
{
Object.defineProperty(MyError.prototype, attr, {
get: function()
{
return this.wrapped[attr];
}
});
}
MyError.prototype = Object.create(Error.prototype);
MyError.prototype.constructor = MyError;
wrap('name');
wrap('message');
wrap('stack');
wrap('fileName');
wrap('lineNumber');
wrap('columnNumber');
MyError.prototype.toString = function()
{
return this.wrapped.toString();
};
Une solution incroyablement élégante, merci de la partager ! Une variante : new MyErr (arg1, arg2, new Error())
et dans le constructeur de MyErr nous utilisons Object.assign
pour affecter les propriétés de la dernière arg à this
Dans l'exemple ci-dessus Error.apply
(également Error.call
) ne fait rien pour moi (Firefox 3.6/Chrome 5). J'utilise une solution de contournement :
function MyError(message, fileName, lineNumber) {
var err = new Error();
if (err.stack) {
// remove one stack level:
if (typeof(Components) != 'undefined') {
// Mozilla:
this.stack = err.stack.substring(err.stack.indexOf('\n')+1);
}
else if (typeof(chrome) != 'undefined' || typeof(process) != 'undefined') {
// Google Chrome/Node.js:
this.stack = err.stack.replace(/\n[^\n]*/,'');
}
else {
this.stack = err.stack;
}
}
this.message = message === undefined ? err.message : message;
this.fileName = fileName === undefined ? err.fileName : fileName;
this.lineNumber = lineNumber === undefined ? err.lineNumber : lineNumber;
}
MyError.prototype = new Error();
MyError.prototype.constructor = MyError;
MyError.prototype.name = 'MyError';
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.