¿Puedo definir tipos personalizados para excepciones definidas por el usuario en JavaScript? Si es así, ¿cómo lo haría?
¿Puedo definir tipos personalizados para excepciones definidas por el usuario en JavaScript? Si es así, ¿cómo lo haría?
Respuestas:
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; } }
⦃⦄ o catch (e) { switch (e.constructor) { case TypeError: …; break; default: throw e; }
⦃⦄.
Debe crear una excepción personalizada que herede prototípicamente 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;
Esta es básicamente una versión simplificada de lo que desapareció publicado 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á 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!
Puede 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 detectar 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");
}
Si. 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 tírelo. La referencia de Javascript de Mozilla tiene varios ejemplos.
function MyError(message) {
this.message = message;
}
MyError.prototype = new Error;
Esto permite el uso como ...
try {
something();
} catch(e) {
if(e instanceof MyError)
doSomethingElse();
else if(e instanceof Error)
andNowForSomethingCompletelyDifferent();
}
e instanceof Error
sería falso.
e instanceof MyError
sería cierto, la else if(e instanceof Error)
declaración nunca sería evaluada.
else if(e instanceof Error)
sería la última captura? Probablemente seguido de un simple else
(que no incluí). Algo así como default:
en una declaración de cambio pero para errores.
En breve:
Si está utilizando ES6 sin transpiladores :
class CustomError extends Error { /* ... */}
Consulte Error de extensión en Javascript con sintaxis ES6 para conocer cuál es la mejor práctica actual
Si está utilizando el transpilador Babel :
Opción 1: use babel-plugin-transform-builtin-extend
Opción 2: hágalo usted 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: use 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, que no se puede sacar ninguna incorporadas en el lenguaje JavaScript , como Date
, Array
, DOM
o Error
.
El problema se describe aquí:
¿Qué pasa con las otras respuestas SO?
Todas las respuestas dadas solucionan el instanceof
problema, 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 que usando el método mencionado anteriormente, no solo soluciona el instanceof
problema 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
Aquí es cómo puede crear errores personalizados con un Error
comportamiento completamente idéntico al del nativo . Esta técnica solo funciona en Chrome y node.js por ahora. Yo también no recomendaría usarlo si usted no entiende 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 use NotImplementedError
como lo haría Error
:
throw new NotImplementedError();
var err = new NotImplementedError();
var err = NotImplementedError('Not yet...');
Y se comportará 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 eso error.stack
funciona absolutamente correcto y no incluirá la NotImplementedError
llamada del constructor (gracias a v8Error.captureStackTrace()
).
Nota. Hay feo eval()
. La única razón por la que se usa es para corregirlo err.constructor.name
. Si no lo necesita, puede simplificar un poco todo.
Error.apply(self, arguments)
Se especifica que no funciona . Sugiero copiar el seguimiento de la pila, que es compatible con todos los navegadores.
A menudo uso un enfoque con herencia prototípica. La anulación 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 excepciones no detectadas.
Utilizar instanceof
para determinar el tipo de excepción.
// 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 el tiro declaración de .
A JavaScript no le importa cuál es el tipo de excepción (como lo hace Java). JavaScript solo se da cuenta, hay una excepción y cuando la detecta, puede "mirar" lo que dice la excepción ".
Si tiene diferentes tipos de excepción que debe lanzar, le sugiero que use variables que contengan la cadena / objeto de la excepción, es decir, el mensaje. Cuando 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 múltiples errores (¡pruebe el código aquí !):
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 Error en JavaScript?
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;
}
}
//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. Al hacer el error más razonable.