¿Cuál es una buena forma de extender Error en JavaScript?

Resuelto Josh Gibson asked hace 15 años • 0 respuestas

Quiero incluir algunas cosas en mi código JS y quiero que sean una instancia de Error, pero también quiero que sean otra cosa.

En Python, normalmente, se subclasificaría Exception.

¿Qué es lo apropiado que se debe hacer en JS?

Josh Gibson avatar Sep 05 '09 07:09 Josh Gibson
Aceptado

En ES6:

class MyError extends Error {
  constructor(message) {
    super(message);
    this.name = 'MyError';
  }
}

fuente

Mohsen avatar Sep 23 '2015 22:09 Mohsen

El único campo estándar que tiene el objeto Error es la messagepropiedad. (Consulte MDN , o Especificación del lenguaje EcmaScript, sección 15.11). Todo lo demás es específico de la plataforma.

La mayoría de los entornos establecen la stackpropiedad, pero fileNamey lineNumberson prácticamente inútiles para usarse 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 olfatear la pila, eliminar elementos no deseados de ella y extraer información como nombre de archivo y número de línea, pero hacerlo requiere información sobre la plataforma en la que se ejecuta JavaScript actualmente. En la mayoría de los casos esto es innecesario, y puedes hacerlo post-mortem si realmente lo deseas.

Safari es una excepción notable. No hay ninguna stackpropiedad, sino los throwconjuntos de palabras clave sourceURLy linelas propiedades del objeto que se está lanzando. Se garantiza que esas cosas serán correctas.

Los casos de prueba que utilicé se pueden encontrar aquí: Comparación de objetos de error propios de JavaScript .

Tero avatar Mar 09 '2011 20:03 Tero

En breve:

  • Si está utilizando ES6 sin transpiladores :

    class CustomError extends Error { /* ... */}
    
  • Si está utilizando el transpilador 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) {
      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: utilizar el marco clastrofóbico

Explicación:

¿Por qué es un problema ampliar la clase Error usando ES6 y Babel?

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, según la documentación oficial de Babel, no puede ampliar ninguna clase de JavaScript integrada como Date, Arrayo .DOMError

El problema se describe aquí:

  • Las extensiones nativas rompen HTMLELement, Array y otros
  • un objeto de la clase que se extiende por tipo base como Matriz, Número, Objeto, Cadena o Error no es una instancia de esta clase

¿Qué pasa con las otras respuestas SO?

Todas las respuestas dadas solucionan el instanceofproblema pero se pierde el error habitual 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 utiliza el método mencionado anteriormente, no solo soluciona el instanceofproblema sino que también mantiene el error habitual console.log:

console.log(new CustomError('test'));
// output:
// Error: test
//     at CustomError (<anonymous>:2:32)
//     at <anonymous>:1:5
JBE avatar Apr 24 '2017 18:04 JBE