Quiero incluir algunas cosas en mi código JS y quiero que sean instancias de Error, pero también quiero que sean otra cosa.
En Python, típicamente, se subclasificaría Exception.
¿Qué es lo apropiado para hacer en JS?
Quiero incluir algunas cosas en mi código JS y quiero que sean instancias de Error, pero también quiero que sean otra cosa.
En Python, típicamente, se subclasificaría Exception.
¿Qué es lo apropiado para hacer en JS?
Respuestas:
El único campo estándar que tiene el objeto Error es la message
propiedad. (Consulte MDN , o Especificación del lenguaje EcmaScript, sección 15.11) Todo lo demás es específico de la plataforma.
Ambientes mosts establecen la stack
propiedad, pero fileName
y lineNumber
son prácticamente inútiles para ser utilizado en herencia.
Entonces, el enfoque minimalista es:
function MyError(message) {
this.name = 'MyError';
this.message = message;
this.stack = (new Error()).stack;
}
MyError.prototype = new Error; // <-- remove this if you do not
// want MyError to be instanceof Error
Puede oler la pila, quitar elementos no deseados de ella y extraer información como fileName y lineNumber, pero para hacerlo se requiere información sobre la plataforma en la que se ejecuta JavaScript actualmente. La mayoría de los casos es innecesario, y puede hacerlo en la autopsia si realmente lo desea.
Safari es una notable excepción. No hay ninguna stack
propiedad, pero los throw
conjuntos de palabras clave sourceURL
y las line
propiedades del objeto que se está lanzando. Esas cosas están garantizadas para ser correctas.
Los casos de prueba que utilicé se pueden encontrar aquí: comparación de objetos de error de JavaScript hecho a sí mismo .
function MyError(message) { this.message = message; this.stack = Error().stack; } MyError.prototype = Object.create(Error.prototype); MyError.prototype.name = "MyError";
MyError.prototype.constructor = MyError
también agregaría .
this
, ¿verdad?
En ES6:
class MyError extends Error {
constructor(message) {
super(message);
this.name = 'MyError';
}
}
var supportsClasses = false; try {eval('class X{}'); supportsClasses = true;} catch (e) {}
this.name = this.constructor.name;
en su lugar.
En breve:
Si está utilizando ES6 sin transpiladores :
class CustomError extends Error { /* ... */}
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) {
var 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 de 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 ampliar ninguna clase de JavaScript incorporada. , 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
class CustomError extends Error { /* ... */}
no maneja correctamente los argumentos específicos del proveedor ( lineNumber
, etc.), 'Extending Error in Javascript with ES6 syntax' es específico de Babel, su solución ES5 usa const
y no maneja argumentos personalizados.
console.log(new CustomError('test') instanceof CustomError);// false
era cierto en el momento de la escritura, pero ahora se ha resuelto. De hecho, el problema vinculado en la respuesta se ha resuelto y podemos probar el comportamiento correcto aquí y pegando el código en el REPL y viendo cómo se transpira correctamente para crear una instancia con la cadena de prototipo correcta.
Editar: favor lea los comentarios. Resulta que esto solo funciona bien en V8 (Chrome / Node.JS). Mi intención era proporcionar una solución de navegador cruzado, que funcionaría en todos los navegadores, y proporcionar un seguimiento de la pila donde hay soporte.
Editar: hice este Wiki de la comunidad para permitir más edición.
La solución para V8 (Chrome / Node.JS), funciona en Firefox y se puede modificar para que funcione principalmente en IE. (ver final de la publicación)
function UserError(message) {
this.constructor.prototype.__proto__ = Error.prototype // Make this an instanceof Error.
Error.call(this) // Does not seem necessary. Perhaps remove this line?
Error.captureStackTrace(this, this.constructor) // Creates the this.stack getter
this.name = this.constructor.name; // Used to cause messages like "UserError: message" instead of the default "Error: message"
this.message = message; // Used to set the message
}
Publicación original en "¡Muéstrame el código!"
Version corta:
function UserError(message) {
this.constructor.prototype.__proto__ = Error.prototype
Error.captureStackTrace(this, this.constructor)
this.name = this.constructor.name
this.message = message
}
Mantengo this.constructor.prototype.__proto__ = Error.prototype
dentro de la función para mantener todo el código junto. Pero también puede reemplazar this.constructor
con UserError
y eso le permite mover el código fuera de la función, por lo que solo se llama una vez.
Si va por esa ruta, asegúrese de llamar a esa línea antes de la primera vez que lance UserError
.
Esa advertencia no aplica la función, porque las funciones se crean primero, sin importar el orden. Por lo tanto, puede mover la función al final del archivo, sin ningún problema.
Compatibilidad del navegador
Funciona en Firefox y Chrome (y Node.JS) y cumple todas las promesas.
Internet Explorer falla en lo siguiente
Los errores no tienen err.stack
que comenzar, así que "no es mi culpa".
Error.captureStackTrace(this, this.constructor)
no existe, por lo que debes hacer otra cosa como
if(Error.captureStackTrace) // AKA if not IE
Error.captureStackTrace(this, this.constructor)
toString
deja de existir cuando subclases Error
. Entonces también necesitas agregar.
else
this.toString = function () { return this.name + ': ' + this.message }
IE no lo considerará UserError
como una instanceof Error
opción a menos que ejecute lo siguiente algún tiempo antes dethrow UserError
UserError.prototype = Error.prototype
Error.call(this)
de hecho no está haciendo nada, ya que devuelve un error en lugar de modificar this
.
UserError.prototype = Error.prototype
es engañoso. Esto no hace herencia, esto los convierte en la misma clase .
Object.setPrototypeOf(this.constructor.prototype, Error.prototype)
es preferible this.constructor.prototype.__proto__ = Error.prototype
, al menos para los navegadores actuales.
Para evitar la repetición de cada tipo diferente de error, combiné la sabiduría de algunas de las soluciones en una createErrorType
función:
function createErrorType(name, init) {
function E(message) {
if (!Error.captureStackTrace)
this.stack = (new Error()).stack;
else
Error.captureStackTrace(this, this.constructor);
this.message = message;
init && init.apply(this, arguments);
}
E.prototype = new Error();
E.prototype.name = name;
E.prototype.constructor = E;
return E;
}
Luego puede definir nuevos tipos de error fácilmente de la siguiente manera:
var NameError = createErrorType('NameError', function (name, invalidChar) {
this.message = 'The name ' + name + ' may not contain ' + invalidChar;
});
var UnboundError = createErrorType('UnboundError', function (variableName) {
this.message = 'Variable ' + variableName + ' is not bound';
});
this.name = name;
?
name
que ya está configurado en el prototipo, ya no es necesario. Lo quité. ¡Gracias!
En 2018 , creo que esta es la mejor manera; que admite IE9 + y navegadores modernos.
ACTUALIZACIÓN : Consulte esta prueba y el repositorio para comparar las diferentes implementaciones.
function CustomError(message) {
Object.defineProperty(this, 'name', {
enumerable: false,
writable: false,
value: 'CustomError'
});
Object.defineProperty(this, 'message', {
enumerable: false,
writable: true,
value: message
});
if (Error.hasOwnProperty('captureStackTrace')) { // V8
Error.captureStackTrace(this, CustomError);
} else {
Object.defineProperty(this, 'stack', {
enumerable: false,
writable: false,
value: (new Error(message)).stack
});
}
}
if (typeof Object.setPrototypeOf === 'function') {
Object.setPrototypeOf(CustomError.prototype, Error.prototype);
} else {
CustomError.prototype = Object.create(Error.prototype, {
constructor: { value: CustomError }
});
}
También tenga en cuenta que la __proto__
propiedad está en desuso, que se usa ampliamente en otras respuestas.
setPrototypeOf()
? Al menos según MDN, generalmente se desaconseja usarlo si puede lograr lo mismo simplemente configurando la .prototype
propiedad en el constructor (como lo está haciendo en el else
bloque para los navegadores que no tienen setPrototypeOf
).
setPrototypeOf
. Pero si aún lo necesita (como lo pide OP), debe usar la metodología incorporada. Como indica MDN, esta se considera la forma correcta de establecer el prototipo de un objeto. En otras palabras, MDN dice que no cambie el prototipo (ya que afecta el rendimiento y la optimización), pero si es necesario, úselo setPrototypeOf
.
CustomError.prototype = Object.create(Error.prototype)
). Además, Object.setPrototypeOf(CustomError, Error.prototype)
está configurando el prototipo del propio constructor en lugar de especificar el prototipo para nuevas instancias de CustomError
. De todos modos, en 2016 creo que en realidad hay una mejor manera de extender los errores, aunque todavía estoy descubriendo cómo usarlo junto con Babel: github.com/loganfsmyth/babel-plugin-transform-builtin-extend/…
CustomError.prototype = Object.create(Error.prototype)
También está cambiando el prototipo. Debe cambiarlo ya que no hay una lógica de extensión / herencia incorporada en ES5. Estoy seguro de que el complemento de Babel que mencionas hace cosas similares.
Object.setPrototypeOf
no tiene sentido aquí, al menos no en la forma en que lo está usando: gist.github.com/mbrowne/4af54767dcb3d529648f5a8aa11d6348 . Tal vez quisiste escribir Object.setPrototypeOf(CustomError.prototype, Error.prototype)
, eso tendría un poco más de sentido (aunque todavía no proporciona ningún beneficio sobre la configuración CustomError.prototype
).
En aras de la integridad, solo porque ninguna de las respuestas anteriores mencionó este método, si está trabajando con Node.js y no tiene que preocuparse por la compatibilidad del navegador, el efecto deseado es bastante fácil de lograr con el incorporado inherits
del util
módulo ( documentos oficiales aquí ).
Por ejemplo, supongamos que desea crear una clase de error personalizada que tome un código de error como primer argumento y el mensaje de error como segundo argumento:
archivo custom-error.js :
'use strict';
var util = require('util');
function CustomError(code, message) {
Error.captureStackTrace(this, CustomError);
this.name = CustomError.name;
this.code = code;
this.message = message;
}
util.inherits(CustomError, Error);
module.exports = CustomError;
Ahora puede crear instancias y pasar / lanzar su CustomError
:
var CustomError = require('./path/to/custom-error');
// pass as the first argument to your callback
callback(new CustomError(404, 'Not found!'));
// or, if you are working with try/catch, throw it
throw new CustomError(500, 'Server Error!');
Tenga en cuenta que, con este fragmento, el seguimiento de la pila tendrá el nombre y la línea correctos del archivo, ¡y la instancia de error tendrá el nombre correcto!
Esto sucede debido al uso del captureStackTrace
método, que crea una stack
propiedad en el objeto de destino (en este caso, la CustomError
instanciación). Para obtener más detalles sobre cómo funciona, consulte la documentación aquí .
this.message = this.message;
¿Esto está mal o todavía hay cosas locas que no sé sobre JS?
La respuesta de Crescent Fresh, la respuesta altamente votada, es engañosa. Aunque sus advertencias no son válidas, hay otras limitaciones que no aborda.
Primero, el razonamiento en el párrafo "Advertencias:" de Crescent no tiene sentido. La explicación implica que codificar "un montón de if (error instanceof MyError) else ..." es de alguna manera oneroso o detallado en comparación con las múltiples declaraciones catch. Múltiples instancias de declaraciones en un solo bloque catch son tan concisas como múltiples declaraciones catch: código limpio y conciso sin ningún truco. Esta es una excelente manera de emular el excelente manejo de errores específicos de subtipo de lanzamiento de Java.
WRT "aparece la propiedad del mensaje de la subclase no se establece", ese no es el caso si utiliza una subclase de error construida correctamente. Para crear su propia subclase de ErrorX Error, simplemente copie el bloque de código que comienza con "var MyError =", cambiando la palabra "MyError" a "ErrorX". (Si desea agregar métodos personalizados a su subclase, siga el texto de muestra).
La limitación real y significativa de la subclasificación de errores de JavaScript es que para las implementaciones o depuradores de JavaScript que rastrean e informan sobre el seguimiento de la pila y la ubicación de la instancia, como FireFox, una ubicación en su propia implementación de subclase de error se registrará como el punto de instanciación de clase, mientras que si usó un error directo, sería la ubicación donde ejecutó "nuevo error (...)"). Los usuarios de IE probablemente nunca lo notarán, pero los usuarios de Fire Bug en FF verán valores inútiles de nombre de archivo y número de línea junto con estos Errores, y tendrán que profundizar en el seguimiento de la pila hasta el elemento # 1 para encontrar la ubicación de instanciación real.
Crescent Fresh's
se ha eliminado!
¿Qué tal esta solución?
En lugar de lanzar su error personalizado usando:
throw new MyError("Oops!");
Envolvería el objeto Error (algo así como un Decorador):
throw new MyError(Error("Oops!"));
Esto asegura que todos los atributos sean correctos, como la pila, fileName lineNumber, etc.
Todo lo que tiene que hacer es copiar los atributos o definir captadores para ellos. Aquí hay un ejemplo usando getters (IE9):
function MyError(wrapped)
{
this.wrapped = wrapped;
this.wrapped.name = 'MyError';
}
function wrap(attr)
{
Object.defineProperty(MyError.prototype, attr, {
get: function()
{
return this.wrapped[attr];
}
});
}
MyError.prototype = Object.create(Error.prototype);
MyError.prototype.constructor = MyError;
wrap('name');
wrap('message');
wrap('stack');
wrap('fileName');
wrap('lineNumber');
wrap('columnNumber');
MyError.prototype.toString = function()
{
return this.wrapped.toString();
};
new MyErr (arg1, arg2, new Error())
y en el constructor MyErr usamos Object.assign
para asignar las propiedades del this
Como han dicho algunas personas, es bastante fácil con ES6:
class CustomError extends Error { }
Así que intenté eso dentro de mi aplicación, (Angular, Typecript) y simplemente no funcionó. Después de un tiempo descubrí que el problema proviene de Typecript: O
Ver https://github.com/Microsoft/TypeScript/issues/13965
Es muy inquietante porque si lo haces:
class CustomError extends Error {}
try {
throw new CustomError()
} catch(e) {
if (e instanceof CustomError) {
console.log('Custom error');
} else {
console.log('Basic error');
}
}
En el nodo o directamente en su navegador, mostrará: Custom error
Intente ejecutar eso con TypeScript en su proyecto en el área de juegos de Typecript, se mostrará Basic error
...
La solución es hacer lo siguiente:
class CustomError extends Error {
// we have to do the following because of: https://github.com/Microsoft/TypeScript/issues/13965
// otherwise we cannot use instanceof later to catch a given type
public __proto__: Error;
constructor(message?: string) {
const trueProto = new.target.prototype;
super(message);
this.__proto__ = trueProto;
}
}
Mi solución es más simple que las otras respuestas proporcionadas y no tiene las desventajas.
Conserva la cadena de prototipos de Error y todas las propiedades en Error sin necesidad de un conocimiento específico de ellas. Ha sido probado en Chrome, Firefox, Node e IE11.
La única limitación es una entrada adicional en la parte superior de la pila de llamadas. Pero eso es fácilmente ignorado.
Aquí hay un ejemplo con dos parámetros personalizados:
function CustomError(message, param1, param2) {
var err = new Error(message);
Object.setPrototypeOf(err, CustomError.prototype);
err.param1 = param1;
err.param2 = param2;
return err;
}
CustomError.prototype = Object.create(
Error.prototype,
{name: {value: 'CustomError', enumerable: false}}
);
Ejemplo de uso:
try {
throw new CustomError('Something Unexpected Happened!', 1234, 'neat');
} catch (ex) {
console.log(ex.name); //CustomError
console.log(ex.message); //Something Unexpected Happened!
console.log(ex.param1); //1234
console.log(ex.param2); //neat
console.log(ex.stack); //stacktrace
console.log(ex instanceof Error); //true
console.log(ex instanceof CustomError); //true
}
Para entornos que requieren un polyfil de setPrototypeOf:
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
obj.__proto__ = proto;
return obj;
};
throw CustomError('err')
lugar dethrow new CustomError('err')
En el ejemplo anterior Error.apply
(también Error.call
) no hace nada por mí (Firefox 3.6 / Chrome 5). Una solución alternativa que uso es:
function MyError(message, fileName, lineNumber) {
var err = new Error();
if (err.stack) {
// remove one stack level:
if (typeof(Components) != 'undefined') {
// Mozilla:
this.stack = err.stack.substring(err.stack.indexOf('\n')+1);
}
else if (typeof(chrome) != 'undefined' || typeof(process) != 'undefined') {
// Google Chrome/Node.js:
this.stack = err.stack.replace(/\n[^\n]*/,'');
}
else {
this.stack = err.stack;
}
}
this.message = message === undefined ? err.message : message;
this.fileName = fileName === undefined ? err.fileName : fileName;
this.lineNumber = lineNumber === undefined ? err.lineNumber : lineNumber;
}
MyError.prototype = new Error();
MyError.prototype.constructor = MyError;
MyError.prototype.name = 'MyError';
En Nodo, como han dicho otros, es simple:
class DumbError extends Error {
constructor(foo = 'bar', ...params) {
super(...params);
if (Error.captureStackTrace) {
Error.captureStackTrace(this, DumbError);
}
this.name = 'DumbError';
this.foo = foo;
this.date = new Date();
}
}
try {
let x = 3;
if (x < 10) {
throw new DumbError();
}
} catch (error) {
console.log(error);
}
Solo quiero agregar a lo que otros ya han dicho:
Para asegurarse de que la clase de error personalizada se muestre correctamente en el seguimiento de la pila, debe establecer la propiedad del nombre del prototipo de la clase de error personalizada en la propiedad del nombre de la clase de error personalizada. Esto es lo que quiero decir:
CustomError.prototype = Error.prototype;
CustomError.prototype.name = 'CustomError';
Entonces el ejemplo completo sería:
var CustomError = function(message) {
var err = new Error(message);
err.name = 'CustomError';
this.name = err.name;
this.message = err.message;
//check if there is a stack property supported in browser
if (err.stack) {
this.stack = err.stack;
}
//we should define how our toString function works as this will be used internally
//by the browser's stack trace generation function
this.toString = function() {
return this.name + ': ' + this.message;
};
};
CustomError.prototype = new Error();
CustomError.prototype.name = 'CustomError';
Cuando todo está dicho y hecho, lanzas tu nueva excepción y se ve así (perezosamente intenté esto en las herramientas de desarrollo de Chrome):
CustomError: Stuff Happened. GASP!
at Error.CustomError (<anonymous>:3:19)
at <anonymous>:2:7
at Object.InjectedScript._evaluateOn (<anonymous>:603:39)
at Object.InjectedScript._evaluateAndWrap (<anonymous>:562:52)
at Object.InjectedScript.evaluate (<anonymous>:481:21)
Mis 2 centavos:
a) Porque acceder a la Error.stack
propiedad (como en algunas respuestas) tiene una gran penalización de rendimiento.
b) Porque es solo una línea.
c) Porque la solución en https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error no parece preservar la información de la pila.
//MyError class constructor
function MyError(msg){
this.__proto__.__proto__ = Error.apply(null, arguments);
};
ejemplo de uso
http://jsfiddle.net/luciotato/xXyeB/
this.__proto__.__proto__
es decir MyError.prototype.__proto__
, está configurando __proto__
PARA TODAS LAS INSTANCIAS de MyError en un error específico recién creado. Mantiene las propiedades y métodos de la clase MyError y también coloca las nuevas propiedades de error (incluido .stack) en la __proto__
cadena.
No puede tener más de una instancia de MyError con información útil de la pila.
No use esta solución si no comprende completamente lo que this.__proto__.__proto__=
hace.
Como las excepciones de JavaScript son difíciles de subclasificar, no lo hago. Acabo de crear una nueva clase de excepción y uso un error dentro de ella. Cambio la propiedad Error.name para que se vea como mi excepción personalizada en la consola:
var InvalidInputError = function(message) {
var error = new Error(message);
error.name = 'InvalidInputError';
return error;
};
La nueva excepción anterior se puede generar como un error normal y funcionará como se espera, por ejemplo:
throw new InvalidInputError("Input must be a string");
// Output: Uncaught InvalidInputError: Input must be a string
Advertencia: el seguimiento de la pila no es perfecto, ya que te llevará a donde se crea el nuevo Error y no a donde lo arrojas. Esto no es un gran problema en Chrome porque le proporciona un seguimiento completo de la pila directamente en la consola. Pero es más problemático en Firefox, por ejemplo.
m = new InvalidInputError(); dontThrowMeYet(m);
m = new ...
a continuación Promise.reject(m)
. No es necesario, pero el código es más fácil de leer.
Como se señaló en la respuesta de Mohsen, en ES6 es posible extender errores usando clases. Es mucho más fácil y su comportamiento es más consistente con los errores nativos ... pero desafortunadamente no es una cuestión simple usar esto en el navegador si necesita soportar navegadores anteriores a ES6. Consulte a continuación algunas notas sobre cómo se podría implementar, pero mientras tanto sugiero un enfoque relativamente simple que incorpore algunas de las mejores sugerencias de otras respuestas:
function CustomError(message) {
//This is for future compatibility with the ES6 version, which
//would display a similar message if invoked without the
//`new` operator.
if (!(this instanceof CustomError)) {
throw new TypeError("Constructor 'CustomError' cannot be invoked without 'new'");
}
this.message = message;
//Stack trace in V8
if (Error.captureStackTrace) {
Error.captureStackTrace(this, CustomError);
}
else this.stack = (new Error).stack;
}
CustomError.prototype = Object.create(Error.prototype);
CustomError.prototype.name = 'CustomError';
En ES6 es tan simple como:
class CustomError extends Error {}
... y puede detectar la compatibilidad con las clases de ES6 try {eval('class X{}')
, pero obtendrá un error de sintaxis si intenta incluir la versión de ES6 en un script cargado por navegadores antiguos. Por lo tanto, la única forma de admitir todos los navegadores sería cargar una secuencia de comandos separada dinámicamente (por ejemplo, a través de AJAX o eval()
) para navegadores que admitan ES6. Una complicación adicional es que eval()
no se admite en todos los entornos (debido a las Políticas de seguridad de contenido), lo que puede o no ser una consideración para su proyecto.
Entonces, por ahora, ya sea el primer enfoque anterior o simplemente usarlo Error
directamente sin tratar de extenderlo, parece ser lo mejor que prácticamente se puede hacer para el código que necesita admitir navegadores que no sean ES6.
Hay otro enfoque que algunas personas pueden considerar, que es usar Object.setPrototypeOf()
donde esté disponible para crear un objeto de error que sea una instancia de su tipo de error personalizado pero que se vea y se comporte más como un error nativo en la consola (gracias a la respuesta de Ben para la recomendación). Aquí está mi opinión sobre ese enfoque: https://gist.github.com/mbrowne/fe45db61cea7858d11be933a998926a8 . Pero dado que algún día podremos usar ES6, personalmente no estoy seguro de que la complejidad de ese enfoque valga la pena.
La forma de hacer esto bien es devolver el resultado de la aplicación desde el constructor, así como configurar el prototipo de la manera complicada habitual de javascripty:
function MyError() {
var tmp = Error.apply(this, arguments);
tmp.name = this.name = 'MyError'
this.stack = tmp.stack
this.message = tmp.message
return this
}
var IntermediateInheritor = function() {}
IntermediateInheritor.prototype = Error.prototype;
MyError.prototype = new IntermediateInheritor()
var myError = new MyError("message");
console.log("The message is: '"+myError.message+"'") // The message is: 'message'
console.log(myError instanceof Error) // true
console.log(myError instanceof MyError) // true
console.log(myError.toString()) // MyError: message
console.log(myError.stack) // MyError: message \n
// <stack trace ...>
Los únicos problemas con esta forma de hacerlo en este momento (lo he iterado un poco) son que
stack
y message
no están incluidas en MyError
yEl primer problema podría solucionarse iterando a través de todas las propiedades de error no enumerables utilizando el truco de esta respuesta: ¿Es posible obtener los nombres de propiedad heredados no enumerables de un objeto? , pero esto no es compatible con ie <9. El segundo problema podría resolverse arrancando esa línea en el seguimiento de la pila, pero no estoy seguro de cómo hacerlo de manera segura (¿tal vez solo quitando la segunda línea de e.stack.toString () ??).
El fragmento lo muestra todo.
function add(x, y) {
if (x && y) {
return x + y;
} else {
/**
*
* the error thrown will be instanceof Error class and InvalidArgsError also
*/
throw new InvalidArgsError();
// throw new Invalid_Args_Error();
}
}
// Declare custom error using using Class
class Invalid_Args_Error extends Error {
constructor() {
super("Invalid arguments");
Error.captureStackTrace(this);
}
}
// Declare custom error using Function
function InvalidArgsError(message) {
this.message = `Invalid arguments`;
Error.captureStackTrace(this);
}
// does the same magic as extends keyword
Object.setPrototypeOf(InvalidArgsError.prototype, Error.prototype);
try{
add(2)
}catch(e){
// true
if(e instanceof Error){
console.log(e)
}
// true
if(e instanceof InvalidArgsError){
console.log(e)
}
}
Daría un paso atrás y consideraría por qué quieres hacer eso. Creo que el punto es tratar los diferentes errores de manera diferente.
Por ejemplo, en Python, puede restringir la instrucción catch a solo catch MyValidationError
, y tal vez quiera hacer algo similar en javascript.
catch (MyValidationError e) {
....
}
No puedes hacer esto en javascript. Solo habrá un bloque de captura. Se supone que debe usar una declaración if en el error para determinar su tipo.
catch(e) {
if(isMyValidationError(e)) {
...
} else {
// maybe rethrow?
throw e;
}
}
Creo que en su lugar arrojaría un objeto sin formato con un tipo, mensaje y cualquier otra propiedad que considere adecuada.
throw { type: "validation", message: "Invalid timestamp" }
Y cuando captas el error:
catch(e) {
if(e.type === "validation") {
// handle error
}
// re-throw, or whatever else
}
error.stack
, las herramientas estándar no funcionarán con él, etc. Una mejor manera sería agregar propiedades a una instancia de error, por ejemplovar e = new Error(); e.type = "validation"; ...
Esto se basa en la respuesta de George Bailey , pero amplía y simplifica la idea original. Está escrito en CoffeeScript, pero es fácil de convertir a JavaScript. La idea es extender el error personalizado de Bailey con un decorador que lo envuelve, lo que le permite crear nuevos errores personalizados fácilmente.
Nota: Esto solo funcionará en V8. No hay soporte para Error.captureStackTrace
en otros entornos.
El decorador toma un nombre para el tipo de error y devuelve una función que toma un mensaje de error y encierra el nombre del error.
CoreError = (@message) ->
@constructor.prototype.__proto__ = Error.prototype
Error.captureStackTrace @, @constructor
@name = @constructor.name
BaseError = (type) ->
(message) -> new CoreError "#{ type }Error: #{ message }"
Ahora es sencillo crear nuevos tipos de error.
StorageError = BaseError "Storage"
SignatureError = BaseError "Signature"
Por diversión, ahora podría definir una función que arroje un SignatureError
si se llama con demasiados argumentos.
f = -> throw SignatureError "too many args" if arguments.length
Esto se ha probado bastante bien y parece funcionar perfectamente en V8, manteniendo el rastreo, la posición, etc.
Nota: El uso new
es opcional al construir un error personalizado.
si no te importan las interpretaciones por errores, esto es lo más pequeño que puedes hacer
Object.setPrototypeOf(MyError.prototype, Error.prototype)
function MyError(message) {
const error = new Error(message)
Object.setPrototypeOf(error, MyError.prototype);
return error
}
puedes usarlo sin nuevo solo MyError (mensaje)
Al cambiar el prototipo después de que se llama el error del constructor, no tenemos que establecer la pila de llamadas y el mensaje
Mohsen tiene una gran respuesta arriba en ES6 que establece el nombre, pero si está utilizando TypeScript o si está viviendo en el futuro, con suerte, esta propuesta para los campos de clase públicos y privados ha pasado la etapa 3 como una propuesta y lo hizo en la etapa 4 como parte de ECMAScript / JavaScript, entonces es posible que desee saber que esto es un poco más corto. La etapa 3 es donde los navegadores comienzan a implementar funciones, por lo que si su navegador lo admite, el siguiente código podría funcionar. (Probado en el nuevo navegador Edge v81 parece funcionar bien). Tenga en cuenta que esta es una característica inestable en este momento y debe usarse con precaución y siempre debe verificar el soporte del navegador en las características inestables. Esta publicación es principalmente para aquellos futuros habitantes cuando los navegadores pueden admitir esto. Para verificar el soporte, consulte MDN y¿Puedo utilizar . Actualmente tiene un 66% de soporte en todo el mercado de navegadores que está llegando allí, pero no es tan bueno, así que si realmente quieres usarlo ahora y no quieres esperar, usa un transpilador como Babel o algo como TypeScript .
class EOFError extends Error {
name="EOFError"
}
throw new EOFError("Oops errored");
Compare esto con un error sin nombre que cuando se lanza no registrará su nombre.
class NamelessEOFError extends Error {}
throw new NamelessEOFError("Oops errored");
this.name = 'MyError'
exterior de la función y cambiarla aMyError.prototype.name = 'MyError'
.