node - Excepciones personalizadas en JavaScript
manejo de errores node js (13)
¿Puedo definir tipos personalizados para excepciones definidas por el usuario en JavaScript? Si puedo, ¿cómo lo haría?
A menudo utilizo un enfoque con herencia prototípica. La anulación de toString()
le brinda la ventaja de que herramientas como Firebug registrarán la información real en lugar de [object Object]
en la consola para las excepciones no detectadas.
Utilice instanceof
para determinar el tipo de excepción.
main.js
// 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
Exception.js
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;
};
DuplicateIdException.js
ns.DuplicateIdException = function(message) {
this.name = ''Duplicate ID'';
this.message = message;
};
ns.DuplicateIdException.prototype = new ns.Exception();
Aquí es cómo puede crear errores personalizados con un comportamiento completamente idéntico al Error
nativo. Esta técnica solo funciona en Chrome y node.js por ahora. Tampoco recomendaría usarlo si no entiendes lo que hace.
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;
}
})();
Como resultado obtenemos
/**
* 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)
Entonces puedes usarlo así:
var NotImplementedError = Error.createCustromConstructor(''NotImplementedError'');
Y usa NotImplementedError
como lo harías con un Error
:
throw new NotImplementedError();
var err = new NotImplementedError();
var err = NotImplementedError(''Not yet...'');
Y se comportará como se espera:
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!
Tenga en cuenta que error.stack
funciona absolutamente correctamente y no incluirá la NotImplementedError
constructor NotImplementedError
(gracias a Error.captureStackTrace()
v8).
Nota. Hay eval()
fea eval()
. La única razón por la que se usa es para obtener el err.constructor.name
correcto.constructor.name. Si no lo necesitas, puedes simplificarlo un poco todo.
Debería crear una excepción personalizada que prototípicamente hereda de Error. Por ejemplo:
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;
Básicamente, se trata de una versión simplificada de lo que se publicó anteriormente con la mejora de que los seguimientos de pila funcionan en Firefox y otros navegadores. Satisface las mismas pruebas que publicó:
Uso:
throw new InvalidArgumentException();
var err = new InvalidArgumentException("Not yet...");
Y se comportará como se espera:
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!
Desde 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:[email protected]/">system administrator</a>.",
toString: function(){return this.name + ": " + this.message;}
};
Podría implementar sus propias excepciones y su manejo, por ejemplo, como aquí:
// 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");
}
}
Hay otra sintaxis para capturar una excepción escrita, aunque esto no funcionará en todos los navegadores (por ejemplo, no en 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í. Puedes lanzar lo que quieras: enteros, cadenas, objetos, lo que sea. Si desea lanzar un objeto, simplemente cree un nuevo objeto, tal como lo haría en otras circunstancias, y luego lance. La referencia de JavaScript de Mozilla tiene varios ejemplos.
Una alternativa a la respuesta de asselin para usar con las clases de ES2015
class InvalidArgumentException extends Error {
constructor(message) {
super();
Error.captureStackTrace(this, this.constructor);
this.name = "InvalidArgumentException";
this.message = message;
}
}
Usa la declaración de throw .
A JavaScript no le importa cuál es el tipo de excepción (como a Java). JavaScript solo se da cuenta, hay una excepción y cuando la detectas, puedes "ver" lo que dice la excepción ".
Si tiene diferentes tipos de excepción que tiene que lanzar, sugeriría usar variables que contengan la cadena / objeto de la excepción, es decir, el mensaje. Donde lo necesite, use "throw myException" y en la captura, compare la excepción capturada con myException.
Vea este ejemplo en el MDN.
Si necesita definir varios errores (pruebe el código here ):
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;
El código se copia principalmente de: ¿Cuál es una buena manera de extender el error en JavaScript?
ES6
Con las nuevas palabras clave de clase y extensión ahora es mucho más fácil:
class CustomError extends Error {
constructor(message) {
super(message);
//something
}
}
En breve:
Si está utilizando ES6 sin transpilers :
class CustomError extends Error { /* ... */}
Consulte Extensión de error en Javascript con la sintaxis de ES6 para conocer las mejores prácticas actuales.
Si está utilizando el transpiler de Babel :
Opción 1: usar babel-plugin-transform-builtin-extend
Opción 2: hazlo tú mismo (inspirado en esa misma biblioteca)
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 está utilizando 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: utilizar el marco Classtrophobic .
Explicación:
¿Por qué extender la clase Error usando ES6 y Babel es un problema?
Porque una instancia de CustomError ya no se reconoce como tal.
class CustomError extends Error {}
console.log(new CustomError(''test'') instanceof Error);// true
console.log(new CustomError(''test'') instanceof CustomError);// false
De hecho, a partir de la documentación oficial de Babel, no puede extender ninguna clase incorporada de JavaScript como Date
, Array
, DOM
o Error
.
El problema se describe aquí:
- Native extiende saltos HTMLELement, Array, y otros
- un objeto de La clase que se extiende por tipo de base como Array, Número, Objeto, Cadena o Error no es una instancia de esta clase
¿Qué pasa con las otras respuestas de SO?
Todas las respuestas dadas solucionan el problema de instanceof
pero pierde el error regular console.log
:
console.log(new CustomError(''test''));
// output:
// CustomError {name: "MyError", message: "test", stack: "Error↵ at CustomError (<anonymous>:4:19)↵ at <anonymous>:1:5"}
Mientras usa el método mencionado anteriormente, no solo soluciona el problema de instanceof
, sino que también mantiene el error regular console.log
:
console.log(new CustomError(''test''));
// output:
// Error: test
// at CustomError (<anonymous>:2:32)
// at <anonymous>:1:5
//create error object
var error = new Object();
error.reason="some reason!";
//business function
function exception(){
try{
throw error;
}catch(err){
err.reason;
}
}
Ahora establecemos agregar el motivo o las propiedades que queramos al objeto de error y recuperarlo. Haciendo el error más razonable.
function MyError(message) {
this.message = message;
}
MyError.prototype = new Error;
Esto permite su uso como ...
try {
something();
} catch(e) {
if(e instanceof MyError)
doSomethingElse();
else if(e instanceof Error)
andNowForSomethingCompletelyDifferent();
}