Czy mogę zdefiniować niestandardowe typy dla wyjątków zdefiniowanych przez użytkownika w JavaScript? Jeśli tak, jak mam to zrobić?
Czy mogę zdefiniować niestandardowe typy dla wyjątków zdefiniowanych przez użytkownika w JavaScript? Jeśli tak, jak mam to zrobić?
Odpowiedzi:
Od 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; } }⦃⦄ lub catch (e) { switch (e.constructor) { case TypeError: …; break; default: throw e; }⦃⦄.
Należy utworzyć niestandardowy wyjątek, który prototypowo dziedziczy po błędzie. Na przykład:
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;
Jest to w zasadzie uproszczona wersja tego, co zniechęcony opublikował powyżej, z ulepszeniem, które ślady stosu działają w przeglądarce Firefox i innych przeglądarkach. Spełnia te same testy, które opublikował:
Stosowanie:
throw new InvalidArgumentException();
var err = new InvalidArgumentException("Not yet...");
I będzie się zachowywać, należy się spodziewać:
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!
Możesz wdrożyć własne wyjątki i ich obsługę, na przykład jak tutaj:
// 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");
}
}
Istnieje inna składnia do przechwytywania wpisanego wyjątku, chociaż nie działa to w każdej przeglądarce (na przykład nie w 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");
}
Tak. Możesz rzucać cokolwiek zechcesz: liczby całkowite, łańcuchy, obiekty, cokolwiek. Jeśli chcesz rzucić obiekt, po prostu stwórz nowy obiekt, tak jak zrobiłbyś go w innych okolicznościach, a następnie rzuć nim. Odwołanie do Mozilli w JavaScript zawiera kilka przykładów.
function MyError(message) {
this.message = message;
}
MyError.prototype = new Error;
Pozwala to na użycie takie jak ..
try {
something();
} catch(e) {
if(e instanceof MyError)
doSomethingElse();
else if(e instanceof Error)
andNowForSomethingCompletelyDifferent();
}
e instanceof Errorbyłoby fałszywe.
e instanceof MyErrorbyłoby to prawdą, else if(e instanceof Error)oświadczenie nigdy nie zostanie ocenione.
else if(e instanceof Error)byłby ostatni haczyk. Najprawdopodobniej następuje prosty else(którego nie uwzględniłem). Coś w rodzaju default:instrukcji switch, ale zawiera błędy.
W skrócie:
Jeśli używasz ES6 bez transpilatorów :
class CustomError extends Error { /* ... */}
Zobacz Rozszerzanie błędu w Javascript przy pomocy składni ES6, aby zapoznać się z aktualną najlepszą praktyką
Jeśli używasz transpilatora Babel :
Opcja 1: użyj rozszerzenia Babel-plugin-transform-builtin-extension
Opcja 2: zrób to sam (zainspirowany tą samą biblioteką)
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);
Jeśli używasz czystego ES5 :
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;
}Alternatywnie: użyj szkieletu Classtrophobic
Wyjaśnienie:
Dlaczego rozszerzenie klasy Error za pomocą ES6 i Babel jest problemem?
Ponieważ wystąpienie CustomError nie jest już rozpoznawane jako takie.
class CustomError extends Error {}
console.log(new CustomError('test') instanceof Error);// true
console.log(new CustomError('test') instanceof CustomError);// false
W rzeczywistości, z oficjalnej dokumentacji Babel, to nie może przedłużyć jakikolwiek wbudowanych klas JavaScript , takich jak Date, Array, DOMlub Error.
Problem opisano tutaj:
Co z innymi odpowiedziami SO?
Wszystkie podane odpowiedzi rozwiązują instanceofproblem, ale tracisz regularny błąd console.log:
console.log(new CustomError('test'));
// output:
// CustomError {name: "MyError", message: "test", stack: "Error↵ at CustomError (<anonymous>:4:19)↵ at <anonymous>:1:5"}
Podczas korzystania z metody wspomnianej powyżej nie tylko naprawiasz instanceofproblem, ale także utrzymujesz regularny błąd console.log:
console.log(new CustomError('test'));
// output:
// Error: test
// at CustomError (<anonymous>:2:32)
// at <anonymous>:1:5
Oto, w jaki sposób można tworzyć niestandardowe błędy o całkowicie identycznym Errorzachowaniu natywnym . Technika ta działa tylko w Chrome i node.js teraz. Nie polecałbym go również używać, jeśli nie rozumiesz, co on robi.
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;
}
})();
Jako wynik otrzymujemy
/**
* 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)
Następnie możesz użyć tego w następujący sposób:
var NotImplementedError = Error.createCustromConstructor('NotImplementedError');
I używaj NotImplementedErrortak, jakbyś Error:
throw new NotImplementedError();
var err = new NotImplementedError();
var err = NotImplementedError('Not yet...');
I będzie się zachowywać, należy się spodziewać:
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!
Zauważ, że to error.stackdziała absolutnie poprawnie i nie będzie zawierać NotImplementedErrorwywołania konstruktora (dzięki v8 Error.captureStackTrace()).
Uwaga. Jest brzydka eval(). Jedynym powodem, dla którego jest używany, jest poprawność err.constructor.name. Jeśli go nie potrzebujesz, możesz nieco uprościć wszystko.
Error.apply(self, arguments)jest określony, aby nie działać . Zamiast tego sugeruję skopiowanie śladu stosu, który jest kompatybilny z różnymi przeglądarkami.
Często używam podejścia z dziedziczeniem prototypowym. Przesłonięcie toString()daje tę przewagę, że narzędzia takie jak Firebug będą rejestrować rzeczywiste informacje zamiast [object Object]w konsoli w celu przechwycenia wyjątków.
Służy instanceofdo określania rodzaju wyjątku.
// 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();
Użyj instrukcji throw .
JavaScript nie dba o typ wyjątku (tak jak Java). JavaScript tylko zauważa, istnieje wyjątek, a kiedy go złapiesz, możesz „spojrzeć” na to, co mówi wyjątek.
Jeśli masz różne typy wyjątków, które musisz zgłosić, sugeruję użycie zmiennych zawierających ciąg / obiekt wyjątku, tj. Komunikat. Tam, gdzie jest to potrzebne, użyj „wyrzuć mój wyjątek”, a następnie złap wychwycony wyjątek od mojego wyjątku.
Zobacz ten przykład w MDN.
Jeśli musisz zdefiniować wiele błędów (przetestuj kod tutaj !):
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;
Kod jest w większości kopiowany z: Jaki jest dobry sposób na rozszerzenie Błąd w JavaScript?
Alternatywa dla odpowiedzi asselin do użytku z klasami 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;
}
}
Teraz ustawiamy dodając przyczynę lub dowolne właściwości, które chcemy, do obiektu błędu i pobieramy go. Uczyniając błąd bardziej rozsądnym.