Puis-je définir un type personnalisé pour les exceptions définies par l'utilisateur en JavaScript? Si oui, comment le ferais-je?
Puis-je définir un type personnalisé pour les exceptions définies par l'utilisateur en JavaScript? Si oui, comment le ferais-je?
Réponses:
De WebReference :
throw {
name: "System Error",
level: "Show Stopper",
message: "Error detected. Please contact the system administrator.",
htmlMessage: "Error detected. Please contact the <a href=\"mailto:sysadmin@acme-widgets.com\">system administrator</a>.",
toString: function(){return this.name + ": " + this.message;}
};
catch (e) { if (e instanceof TypeError) { … } else { throw e; } }
⦃⦄ ou catch (e) { switch (e.constructor) { case TypeError: …; break; default: throw e; }
⦃⦄.
Vous devez créer une exception personnalisée qui hérite de manière prototype d'Erreur. Par exemple:
function InvalidArgumentException(message) {
this.message = message;
// Use V8's native method if available, otherwise fallback
if ("captureStackTrace" in Error)
Error.captureStackTrace(this, InvalidArgumentException);
else
this.stack = (new Error()).stack;
}
InvalidArgumentException.prototype = Object.create(Error.prototype);
InvalidArgumentException.prototype.name = "InvalidArgumentException";
InvalidArgumentException.prototype.constructor = InvalidArgumentException;
Ceci est essentiellement une version simplifiée de ce disfated affiché ci-dessus avec l'amélioration que les traces de pile fonctionnent sur Firefox et d'autres navigateurs. Il satisfait aux mêmes tests qu'il a postés:
Usage:
throw new InvalidArgumentException();
var err = new InvalidArgumentException("Not yet...");
Et il va se comporter est attendu:
err instanceof InvalidArgumentException // -> true
err instanceof Error // -> true
InvalidArgumentException.prototype.isPrototypeOf(err) // -> true
Error.prototype.isPrototypeOf(err) // -> true
err.constructor.name // -> InvalidArgumentException
err.name // -> InvalidArgumentException
err.message // -> Not yet...
err.toString() // -> InvalidArgumentException: Not yet...
err.stack // -> works fine!
Vous pouvez implémenter vos propres exceptions et leur gestion par exemple comme ici:
// define exceptions "classes"
function NotNumberException() {}
function NotPositiveNumberException() {}
// try some code
try {
// some function/code that can throw
if (isNaN(value))
throw new NotNumberException();
else
if (value < 0)
throw new NotPositiveNumberException();
}
catch (e) {
if (e instanceof NotNumberException) {
alert("not a number");
}
else
if (e instanceof NotPositiveNumberException) {
alert("not a positive number");
}
}
Il existe une autre syntaxe pour intercepter une exception typée, bien que cela ne fonctionne pas dans tous les navigateurs (par exemple pas dans IE):
// define exceptions "classes"
function NotNumberException() {}
function NotPositiveNumberException() {}
// try some code
try {
// some function/code that can throw
if (isNaN(value))
throw new NotNumberException();
else
if (value < 0)
throw new NotPositiveNumberException();
}
catch (e if e instanceof NotNumberException) {
alert("not a number");
}
catch (e if e instanceof NotPositiveNumberException) {
alert("not a positive number");
}
Oui. Vous pouvez lancer tout ce que vous voulez: des entiers, des chaînes, des objets, peu importe. Si vous souhaitez lancer un objet, créez simplement un nouvel objet, comme vous le feriez dans d'autres circonstances, puis lancez-le. La référence Javascript de Mozilla a plusieurs exemples.
function MyError(message) {
this.message = message;
}
MyError.prototype = new Error;
Cela permet une utilisation comme ..
try {
something();
} catch(e) {
if(e instanceof MyError)
doSomethingElse();
else if(e instanceof Error)
andNowForSomethingCompletelyDifferent();
}
e instanceof Error
serait faux.
e instanceof MyError
cela serait vrai, l' else if(e instanceof Error)
énoncé ne serait jamais évalué.
else if(e instanceof Error)
serait la dernière prise. Probablement suivi d'un simple else
(que je n'ai pas inclus). Un peu comme default:
dans une instruction switch mais pour les erreurs.
En bref:
Si vous utilisez ES6 sans transpilers :
class CustomError extends Error { /* ... */}
Voir Extension de l'erreur en Javascript avec la syntaxe ES6 pour connaître la meilleure pratique actuelle
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)
function CustomError(...args) {
const instance = Reflect.construct(Error, args);
Reflect.setPrototypeOf(instance, Reflect.getPrototypeOf(this));
return instance;
}
CustomError.prototype = Object.create(Error.prototype, {
constructor: {
value: Error,
enumerable: false,
writable: true,
configurable: true
}
});
Reflect.setPrototypeOf(CustomError, Error);
Si vous utilisez ES5 pur :
function CustomError(message, fileName, lineNumber) {
const instance = new Error(message, fileName, lineNumber);
Object.setPrototypeOf(instance, Object.getPrototypeOf(this));
return instance;
}
CustomError.prototype = Object.create(Error.prototype, {
constructor: {
value: Error,
enumerable: false,
writable: true,
configurable: true
}
});
if (Object.setPrototypeOf){
Object.setPrototypeOf(CustomError, Error);
} else {
CustomError.__proto__ = Error;
}
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.
class CustomError extends Error {}
console.log(new CustomError('test') instanceof Error);// true
console.log(new CustomError('test') instanceof CustomError);// false
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
ou Error
.
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ère console.log
:
console.log(new CustomError('test'));
// output:
// CustomError {name: "MyError", message: "test", stack: "Error↵ at CustomError (<anonymous>:4:19)↵ at <anonymous>:1:5"}
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ère console.log
:
console.log(new CustomError('test'));
// output:
// Error: test
// at CustomError (<anonymous>:2:32)
// at <anonymous>:1:5
Voici comment vous pouvez créer des erreurs personnalisées avec un comportement complètement identique au natif Error
. Cette technique ne fonctionne que dans Chrome et node.js pour l'instant. Je ne recommanderais pas non plus de l'utiliser si vous ne comprenez pas ce qu'il fait.
Error.createCustromConstructor = (function() {
function define(obj, prop, value) {
Object.defineProperty(obj, prop, {
value: value,
configurable: true,
enumerable: false,
writable: true
});
}
return function(name, init, proto) {
var CustomError;
proto = proto || {};
function build(message) {
var self = this instanceof CustomError
? this
: Object.create(CustomError.prototype);
Error.apply(self, arguments);
Error.captureStackTrace(self, CustomError);
if (message != undefined) {
define(self, 'message', String(message));
}
define(self, 'arguments', undefined);
define(self, 'type', undefined);
if (typeof init == 'function') {
init.apply(self, arguments);
}
return self;
}
eval('CustomError = function ' + name + '() {' +
'return build.apply(this, arguments); }');
CustomError.prototype = Object.create(Error.prototype);
define(CustomError.prototype, 'constructor', CustomError);
for (var key in proto) {
define(CustomError.prototype, key, proto[key]);
}
Object.defineProperty(CustomError.prototype, 'name', { value: name });
return CustomError;
}
})();
Comme résultat, nous obtenons
/**
* name The name of the constructor name
* init User-defined initialization function
* proto It's enumerable members will be added to
* prototype of created constructor
**/
Error.createCustromConstructor = function(name, init, proto)
Ensuite, vous pouvez l'utiliser comme ceci:
var NotImplementedError = Error.createCustromConstructor('NotImplementedError');
Et utilisez NotImplementedError
comme vous le feriez Error
:
throw new NotImplementedError();
var err = new NotImplementedError();
var err = NotImplementedError('Not yet...');
Et il va se comporter est attendu:
err instanceof NotImplementedError // -> true
err instanceof Error // -> true
NotImplementedError.prototype.isPrototypeOf(err) // -> true
Error.prototype.isPrototypeOf(err) // -> true
err.constructor.name // -> NotImplementedError
err.name // -> NotImplementedError
err.message // -> Not yet...
err.toString() // -> NotImplementedError: Not yet...
err.stack // -> works fine!
Notez que cela error.stack
fonctionne parfaitement et n'inclura pas d' NotImplementedError
appel constructeur (grâce aux v8Error.captureStackTrace()
).
Remarque. Il est moche eval()
. La seule raison pour laquelle il est utilisé est d'être correct err.constructor.name
. Si vous n'en avez pas besoin, vous pouvez tout simplifier un peu.
Error.apply(self, arguments)
est spécifié pour ne pas fonctionner . Je suggère plutôt de copier la trace de la pile qui est compatible avec tous les navigateurs.
J'utilise souvent une approche avec héritage prototypique. PrimordialtoString()
vous donne l'avantage que des outils comme Firebug enregistreront les informations réelles au lieu de[object Object]
sur la console pour les exceptions non détectées.
Utilisation instanceof
pour déterminer le type d'exception.
// just an exemplary namespace
var ns = ns || {};
// include JavaScript of the following
// source files here (e.g. by concatenation)
var someId = 42;
throw new ns.DuplicateIdException('Another item with ID ' +
someId + ' has been created');
// Firebug console:
// uncaught exception: [Duplicate ID] Another item with ID 42 has been created
ns.Exception = function() {
}
/**
* Form a string of relevant information.
*
* When providing this method, tools like Firebug show the returned
* string instead of [object Object] for uncaught exceptions.
*
* @return {String} information about the exception
*/
ns.Exception.prototype.toString = function() {
var name = this.name || 'unknown';
var message = this.message || 'no description';
return '[' + name + '] ' + message;
};
ns.DuplicateIdException = function(message) {
this.name = 'Duplicate ID';
this.message = message;
};
ns.DuplicateIdException.prototype = new ns.Exception();
Utilisez le lancer instruction .
JavaScript ne se soucie pas du type d'exception (comme le fait Java). JavaScript remarque juste, il y a une exception et quand vous l'attrapez, vous pouvez "regarder" ce que l'exception "dit".
Si vous devez lancer différents types d'exceptions, je vous suggère d'utiliser des variables qui contiennent la chaîne / l'objet de l'exception, c'est-à-dire le message. Lorsque vous en avez besoin, utilisez "throw myException" et dans la capture, comparez l'exception capturée à myException.
Voir cet exemple dans le MDN.
Si vous devez définir plusieurs erreurs (testez le code ici !):
function createErrorType(name, initFunction) {
function E(message) {
this.message = message;
if (Error.captureStackTrace)
Error.captureStackTrace(this, this.constructor);
else
this.stack = (new Error()).stack;
initFunction && initFunction.apply(this, arguments);
}
E.prototype = Object.create(Error.prototype);
E.prototype.name = name;
E.prototype.constructor = E;
return E;
}
var InvalidStateError = createErrorType(
'InvalidStateError',
function (invalidState, acceptedStates) {
this.message = 'The state ' + invalidState + ' is invalid. Expected ' + acceptedStates + '.';
});
var error = new InvalidStateError('foo', 'bar or baz');
function assert(condition) { if (!condition) throw new Error(); }
assert(error.message);
assert(error instanceof InvalidStateError);
assert(error instanceof Error);
assert(error.name == 'InvalidStateError');
assert(error.stack);
error.message;
Le code est principalement copié à partir de: Quel est un bon moyen d'étendre l'erreur en JavaScript?
Une alternative à la réponse d' asselin pour une utilisation avec les classes ES2015
class InvalidArgumentException extends Error {
constructor(message) {
super();
Error.captureStackTrace(this, this.constructor);
this.name = "InvalidArgumentException";
this.message = message;
}
}
//create error object
var error = new Object();
error.reason="some reason!";
//business function
function exception(){
try{
throw error;
}catch(err){
err.reason;
}
}
Maintenant, nous définissons ajouter la raison ou les propriétés que nous voulons à l'objet d'erreur et le récupérer. En rendant l'erreur plus raisonnable.