Posso definire un tipo personalizzato per le eccezioni definite dall'utente in JavaScript? Se è così, come lo farei?
Posso definire un tipo personalizzato per le eccezioni definite dall'utente in JavaScript? Se è così, come lo farei?
Risposte:
Da 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; } }
⦃⦄ o catch (e) { switch (e.constructor) { case TypeError: …; break; default: throw e; }
⦃⦄.
È necessario creare un'eccezione personalizzata che eredita prototipicamente da Errore. Per esempio:
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;
Questa è fondamentalmente una versione semplificata di ciò che è sfigurato pubblicato sopra con il miglioramento che le tracce dello stack funzionano su Firefox e altri browser. Soddisfa gli stessi test che ha pubblicato:
Uso:
throw new InvalidArgumentException();
var err = new InvalidArgumentException("Not yet...");
E si comporterà è previsto:
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!
Potresti implementare le tue eccezioni e la loro gestione, ad esempio come qui:
// 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");
}
}
Esiste un'altra sintassi per rilevare un'eccezione digitata, sebbene non funzionerà in tutti i browser (ad esempio non in 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");
}
Sì. Puoi lanciare tutto quello che vuoi: numeri interi, stringhe, oggetti, qualunque cosa. Se vuoi lanciare un oggetto, crea semplicemente un nuovo oggetto, proprio come faresti per crearne uno in altre circostanze, e poi lancialo. Il riferimento Javascript di Mozilla ha diversi esempi.
function MyError(message) {
this.message = message;
}
MyError.prototype = new Error;
Questo consente l'utilizzo come ..
try {
something();
} catch(e) {
if(e instanceof MyError)
doSomethingElse();
else if(e instanceof Error)
andNowForSomethingCompletelyDifferent();
}
e instanceof Error
sarebbe falso.
e instanceof MyError
sarebbe vero, l' else if(e instanceof Error)
affermazione non sarebbe mai stata valutata.
else if(e instanceof Error)
sarebbe l'ultima cattura. Probabilmente seguito da un semplice else
(che non ho incluso). Un po 'come default:
in un'istruzione switch ma per errori.
In breve:
Se si utilizza ES6 senza transpilers :
class CustomError extends Error { /* ... */}
Vedere Estensione dell'errore in Javascript con sintassi ES6 per le migliori pratiche attuali
Se stai usando il transpiler Babel :
Opzione 1: usa babel-plugin-transform-builtin-extension
Opzione 2: fai da te (ispirato alla stessa libreria)
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);
Se si utilizza ES5 puro :
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;
}
Alternativa: utilizzare la struttura Classtrophobic
Spiegazione:
Perché l'estensione della classe Error tramite ES6 e Babel è un problema?
Perché un'istanza di CustomError non è più riconosciuta come tale.
class CustomError extends Error {}
console.log(new CustomError('test') instanceof Error);// true
console.log(new CustomError('test') instanceof CustomError);// false
In effetti, dalla documentazione ufficiale di Babele, tu può non estendersi alcun built-in classi JavaScript come Date
, Array
, DOM
o Error
.
Il problema è descritto qui:
E le altre risposte SO?
Tutte le risposte fornite risolvono il instanceof
problema ma perdi l'errore normale console.log
:
console.log(new CustomError('test'));
// output:
// CustomError {name: "MyError", message: "test", stack: "Error↵ at CustomError (<anonymous>:4:19)↵ at <anonymous>:1:5"}
Considerando che usando il metodo sopra menzionato, non solo risolvi il instanceof
problema ma mantieni anche l'errore regolare console.log
:
console.log(new CustomError('test'));
// output:
// Error: test
// at CustomError (<anonymous>:2:32)
// at <anonymous>:1:5
Ecco come è possibile creare errori personalizzati con un Error
comportamento completamente identico al comportamento nativo . Questa tecnica funziona solo in Chrome e node.js per ora. Inoltre non consiglierei di usarlo se non capisci cosa fa.
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;
}
})();
Come risultato otteniamo
/**
* 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)
Quindi puoi usarlo in questo modo:
var NotImplementedError = Error.createCustromConstructor('NotImplementedError');
E usa NotImplementedError
come faresti Error
:
throw new NotImplementedError();
var err = new NotImplementedError();
var err = NotImplementedError('Not yet...');
E si comporterà è previsto:
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!
Si noti che error.stack
funziona perfettamente e non includeNotImplementedError
chiamata del costruttore (grazie ai v8 Error.captureStackTrace()
).
Nota. C'è brutto eval()
. L'unico motivo per cui viene utilizzato è quello di essere corretti err.constructor.name
. Se non ti serve, puoi semplificare un po 'tutto.
Error.apply(self, arguments)
è specificato per non funzionare . Suggerisco invece di copiare la traccia dello stack che è compatibile con più browser.
Uso spesso un approccio con eredità prototipale. L'override toString()
ti dà il vantaggio che strumenti come Firebug registreranno le informazioni effettive anziché[object Object]
sulla console per eccezioni non rilevate.
Uso instanceof
per determinare il tipo di eccezione.
// 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();
Usa il tiro dichiarazione di .
A JavaScript non importa quale sia il tipo di eccezione (come fa Java). JavaScript si accorge solo che c'è un'eccezione e quando lo catturi, puoi "guardare" cosa dice l'eccezione ".
Se hai diversi tipi di eccezione che devi lanciare, ti suggerirei di usare variabili che contengono la stringa / oggetto dell'eccezione, ad esempio il messaggio. Laddove necessario, usa "usa myException" e, nella cattura, confronta l'eccezione rilevata con myException.
Vedi questo esempio in MDN.
Se devi definire più errori (prova qui il codice !):
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;
Il codice viene copiato principalmente da: qual è un buon modo per estendere l'errore in JavaScript?
//create error object
var error = new Object();
error.reason="some reason!";
//business function
function exception(){
try{
throw error;
}catch(err){
err.reason;
}
}
Ora impostiamo aggiungere il motivo o le proprietà che vogliamo all'oggetto errore e recuperarlo. Rendendo l'errore più ragionevole.