Jak utworzyć niestandardowy błąd w JavaScript?

215

Z jakiegoś powodu wygląda na to, że delegowanie konstruktora nie działa w następującym fragmencie:

function NotImplementedError() { 
  Error.apply(this, arguments); 
}
NotImplementedError.prototype = new Error();

var nie = new NotImplementedError("some message");
console.log("The message is: '"+nie.message+"'")

Uruchomienie tego daje The message is: ''. Wszelkie pomysły, dlaczego lub czy istnieje lepszy sposób na utworzenie nowej Errorpodklasy? Czy jest jakiś problem z applynatywnym Errorkonstruktorem, o którym nie wiem?

cdleary
źródło
Czy po wprowadzeniu zmian nie działa asercja NotImplementedError? Pomyślałem, że aby to zadziałało, musisz jawnie zdefiniować NotImplementedError.prototype.constructor.
jayarjo,
Następnym razem usuń cały dodatkowy kod, który nie jest wymagany do wykazania problemu. Ponadto wtc to js.jar? Czy jest to konieczne do odtworzenia problemu?
BT
2
Zmodyfikowałem to pytanie, aby było zrozumiałe w ciągu 10 sekund, a nie 10 minut
BT
Utworzyłem bibliotekę dziedziczenia / klasy, która poprawnie dziedziczy po typach błędów: github.com/fresheneesz/proto
BT
1
jsfiddle dla kilku najlepszych odpowiedzi.
Nate

Odpowiedzi:

194

Zaktualizuj kod, aby przypisać prototyp do Error.prototype, a instanceof i twoje twierdzenia działają.

function NotImplementedError(message) {
    this.name = "NotImplementedError";
    this.message = (message || "");
}
NotImplementedError.prototype = Error.prototype;

Jednak po prostu rzuciłbym własny obiekt i po prostu sprawdziłbym właściwość name.

throw {name : "NotImplementedError", message : "too lazy to implement"}; 

Edytuj na podstawie komentarzy

Po zapoznaniu się z komentarzami i próbie przypomnienia sobie, dlaczego przypisałem prototyp do Error.prototypezamiast new Error()Nicholasa Zakasa w jego artykule , stworzyłem jsFiddle z poniższym kodem:

function NotImplementedError(message) {
  this.name = "NotImplementedError";
  this.message = (message || "");
}
NotImplementedError.prototype = Error.prototype;

function NotImplementedError2(message) {
  this.message = (message || "");
}
NotImplementedError2.prototype = new Error();

try {
  var e = new NotImplementedError("NotImplementedError message");
  throw e;
} catch (ex1) {
  console.log(ex1.stack);
  console.log("ex1 instanceof NotImplementedError = " + (ex1 instanceof NotImplementedError));
  console.log("ex1 instanceof Error = " + (ex1 instanceof Error));
  console.log("ex1.name = " + ex1.name);
  console.log("ex1.message = " + ex1.message);
}

try {
  var e = new NotImplementedError2("NotImplementedError2 message");
  throw e;
} catch (ex1) {
  console.log(ex1.stack);
  console.log("ex1 instanceof NotImplementedError2 = " + (ex1 instanceof NotImplementedError2));
  console.log("ex1 instanceof Error = " + (ex1 instanceof Error));
  console.log("ex1.name = " + ex1.name);
  console.log("ex1.message = " + ex1.message);
}

Dane wyjściowe konsoli były takie.

undefined
ex1 instanceof NotImplementedError = true
ex1 instanceof Error = true
ex1.name = NotImplementedError
ex1.message = NotImplementedError message
Error
    at window.onload (http://fiddle.jshell.net/MwMEJ/show/:29:34)
ex1 instanceof NotImplementedError2 = true
ex1 instanceof Error = true
ex1.name = Error
ex1.message = NotImplementedError2 message

Potwierdza to, że „problem”, na jaki natrafiłem, był właściwością stosu błędu był numer wiersza, w którym new Error()został utworzony, a nie throw emiejsce wystąpienia. Może to jednak być lepsze niż efekt uboczny NotImplementedError.prototype.name = "NotImplementedError"linii wpływającej na obiekt Error.

Zauważ też NotImplementedError2, że gdy nie ustawiam .namejawnie, jest to równe „Błąd”. Jednak, jak wspomniano w komentarzach, ponieważ ta wersja ustawia prototyp na new Error(), mógłbym ustawić NotImplementedError2.prototype.name = "NotImplementedError2"i być OK.

Kevin Hakanson
źródło
45
Najlepsza odpowiedź, ale Error.prototypebezpośrednie przyjmowanie jest prawdopodobnie złą formą. Jeśli chcesz później dodać NotImplementedError.prototype.toStringobiekt do aliasu Error.prototype.toString- lepiej to zrobić NotImplementedError.prototype = new Error().
cdleary
4
Nadal jestem trochę zagubiony we wszystkich tych prototypowych rzeczach. Dlaczego w twoim przykładzie przypisujesz nazwę to.nazwie, a nie NotImplementedError.prototype.name? Czy możesz mi odpowiedzieć, jest to kluczowe dla mojego zrozumienia :)
jayarjo,
27
Według code.google.com/p/chromium/issues/detail?id=228909 subclass.prototype = new Error() jest złą postacią. Zamiast tego powinieneś użyć subclass.prototype = Object.create(superclass.prototype). Mam nadzieję, że może to również rozwiązać problem śledzenia stosu.
Gili
8
Prostą sztuczką, aby uzyskać znaczący stacktrace, jest wygenerowanie błędu w konstruktorze i zapisanie jego stosu. Dałoby to odpowiedni stos wywołań + 1 linię dla konstruktora (jest to odpowiednia wypłata):this.stack = new Error().stack;
Meredian
6
-1; To jest źle. Robi NotImplementedError.prototype = Error.prototype;, nie czyni instanceoftraktują NotImplementedErrorjako podklasy o Error, to czyni instanceofje traktować jako dokładnie tej samej klasy. Jeśli wkleisz powyższy kod do konsoli i spróbujesz new Error() instanceof NotImplementedErroruzyskać true, co jest oczywiście błędne.
Mark Amery
87

Wszystkie powyższe odpowiedzi są okropne - naprawdę. Nawet ten z 107 wzlotami! Prawdziwa odpowiedź jest tutaj:

Dziedziczenie po obiekcie Error - gdzie jest właściwość message?

TL; DR:

O. messageNie ustawiono powodu, że Errorjest to funkcja, która zwraca nowy obiekt Error i nie manipuluje thisw żaden sposób.

B. Aby to zrobić, należy zwrócić wynik zastosowania z konstruktora, a także ustawić prototyp w zwykły skomplikowany sposób javascripty:

function MyError() {
    var temp = Error.apply(this, arguments);
    temp.name = this.name = 'MyError';
    this.message = temp.message;
    if(Object.defineProperty) {
        // getter for more optimizy goodness
        /*this.stack = */Object.defineProperty(this, 'stack', { 
            get: function() {
                return temp.stack
            },
            configurable: true // so you can change it if you want
        })
    } else {
        this.stack = temp.stack
    }
}
//inherit prototype using ECMAScript 5 (IE 9+)
MyError.prototype = Object.create(Error.prototype, {
    constructor: {
        value: MyError,
        writable: true,
        configurable: true
    }
});

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 ...>


 
//for EMCAScript 4 or ealier (IE 8 or ealier), inherit prototype this way instead of above code:
/*
var IntermediateInheritor = function() {};
IntermediateInheritor.prototype = Error.prototype;
MyError.prototype = new IntermediateInheritor();
*/

Można chyba zrobić kilka sztuczek wyliczyć przez wszystkich non-przeliczalnych właściwości tmpError je ustawić zamiast jawnie ustawienie tylko stacka message, ale oszustwo nie jest obsługiwana w IE <9

BT
źródło
2
To rozwiązanie działa również w przypadku wystąpienia błędu niestandardowego z istniejącym błędem. Jeśli korzystasz z biblioteki innej firmy i chcesz owinąć istniejący błąd własnym niestandardowym typem, inne metody nie działają poprawnie. Do Twojej wiadomości możesz utworzyć Błędy waniliowe, przekazując im istniejący błąd.
Kyle Mueller
1
nie powinieneś być return thisw konstruktorze.
Onur Yıldırım
13
Uprościłem i poprawiłem to podejście: jsbin.com/rolojuhuya/1/edit?js,console
Matt Kantor
3
@MattKantor może udzielić odpowiedzi? Myślę, że najbardziej lubię twoją.
mpoisot
2
Zamiast tego temp.name = this.name = 'MyError'możesz zrobić temp.name = this.name = this.constructor.name. W ten sposób będzie działać również dla podklas MyError.
Jo Liss,
45

W ES2015 możesz classto zrobić czysto:

class NotImplemented extends Error {
  constructor(message = "", ...args) {
    super(message, ...args);
    this.message = message + " has not yet been implemented.";
  }
}

Nie modyfikuje to globalnego Errorprototypu, pozwala dostosować message,name i inne atrybuty, a właściwie oddaje stos. Jest również dość czytelny.

Oczywiście może być konieczne użycie narzędzia, takiego jak babelkod działający w starszych przeglądarkach.

rattray
źródło
23

Jeśli ktoś jest ciekawy, jak utworzyć niestandardowy błąd i uzyskać ślad stosu:

function CustomError(message) {
  this.name = 'CustomError';
  this.message = message || '';
  var error = new Error(this.message);
  error.name = this.name;
  this.stack = error.stack;
}
CustomError.prototype = Object.create(Error.prototype);

try {
  throw new CustomError('foobar');
}
catch (e) {
  console.log('name:', e.name);
  console.log('message:', e.message);
  console.log('stack:', e.stack);
}
risto
źródło
7

Ta sekcja standardu może wyjaśnić, dlaczego Error.applywywołanie nie inicjuje obiektu:

15.11.1 Konstruktor błędów wywoływany jako funkcja

Gdy błąd jest wywoływany raczej jako funkcja niż jako konstruktor, tworzy i inicjuje nowy obiekt Error. Zatem wywołanie funkcji Błąd (...) jest równoważne wyrażeniu tworzenia obiektu nowy Błąd (...) z tymi samymi argumentami.

W takim przypadku Errorfunkcja prawdopodobnie określa, że ​​nie jest wywoływana jako konstruktor, więc zwraca nową instancję Error zamiast zainicjowaćthis obiekt.

Testowanie za pomocą następującego kodu wydaje się wykazywać, że tak właśnie się dzieje:

function NotImplementedError() { 
   var returned = Error.apply(this, arguments);
   console.log("returned.message = '" + returned.message + "'");
   console.log("this.message = '" + this.message + "'");
}
NotImplementedError.prototype = new Error();

var nie = new NotImplementedError("some message");

Po uruchomieniu generowane są następujące dane wyjściowe:

returned.message = 'some message'
this.message = ''
Dave
źródło
jak można to zasymulować za pomocą niestandardowej klasy błędów? Na przykład, w jaki sposób moja niestandardowa klasa błędów może być używana zarówno jako funkcja tworząca instancję, jak i jako konstruktor?
Lea Hayes,
Nie, to nie jest prawda. Jeśli zwróci nową instancję błędu, jego właściwość msg będzie działać.
BT
@BT W jaki sposób właściwość msg w nowej instancji wpływa na właściwość msg w thisin Error.apply(this, arguments);? Mówię, że wezwanie do błędu tutaj buduje nowy obiekt, który jest wyrzucany; nie inicjowanie już zbudowanego obiektu, do którego jest przypisany nie.
Dave
@ BT Dodałem przykładowy kod, który mam nadzieję, że wyjaśni, co próbowałem powiedzieć.
Dave
@Dave Mogłem nie rozumieć tutaj celu, ale czy twoja NotImplementedErrorimplementacja nie powinna zwrócić returnedzmiennej?
blong
7
function InvalidValueError(value, type) {
    this.message = "Expected `" + type.name + "`: " + value;
    var error = new Error(this.message);
    this.stack = error.stack;
}
InvalidValueError.prototype = new Error();
InvalidValueError.prototype.name = InvalidValueError.name;
InvalidValueError.prototype.constructor = InvalidValueError;
Pierre Voisin
źródło
3
To najlepsza odpowiedź tutaj. Jest on prosty, a utworzony w ten sposób wyjątek zachowuje się poprawnie we wszystkich sytuacjach. Zachowuje również ślad stosu, co jest bardzo ważne w nie trywialnych zastosowaniach. Zastąpiłbym tylko „prototype = new Error ()” na „prototype = Object.create (Error.prototype)”. W przypadku Node.js istnieje mała biblioteka, która robi to za Ciebie: npmjs.com/package/node-custom-errors
Łukasz Korzybski
6

Miałem podobny problem do tego. Mój błąd musi być jednym i instanceofdrugim , i musi także generować spójny ślad w konsoli.ErrorNotImplemented

Moje rozwiązanie:

var NotImplemented = (function() {
  var NotImplemented, err;
  NotImplemented = (function() {
    function NotImplemented(message) {
      var err;
      err = new Error(message);
      err.name = "NotImplemented";
      this.message = err.message;
      if (err.stack) this.stack = err.stack;
    }
    return NotImplemented;
  })();
  err = new Error();
  err.name = "NotImplemented";
  NotImplemented.prototype = err;

  return NotImplemented;
}).call(this);

// TEST:
console.log("instanceof Error: " + (new NotImplemented() instanceof Error));
console.log("instanceof NotImplemented: " + (new NotImplemented() instanceofNotImplemented));
console.log("message: "+(new NotImplemented('I was too busy').message));
throw new NotImplemented("just didn't feel like it");

Wynik działania z node.js:

instanceof Error: true
instanceof NotImplemented: true
message: I was too busy

/private/tmp/t.js:24
throw new NotImplemented("just didn't feel like it");
      ^
NotImplemented: just didn't feel like it
    at Error.NotImplemented (/Users/colin/projects/gems/jax/t.js:6:13)
    at Object.<anonymous> (/Users/colin/projects/gems/jax/t.js:24:7)
    at Module._compile (module.js:449:26)
    at Object.Module._extensions..js (module.js:467:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.runMain (module.js:487:10)
    at process.startup.processNextTick.process._tickCallback (node.js:244:9)

Błąd spełnia wszystkie 3 moje kryteria i chociaż stackwłaściwość jest niestandardowa, jest obsługiwana w większości nowszych przeglądarek, co jest dopuszczalne w moim przypadku.

sinisterchipmunk
źródło
5

Zgodnie z Joyentem nie powinieneś zadzierać z właściwością stosu (co widzę w wielu podanych tutaj odpowiedziach), ponieważ będzie to miało negatywny wpływ na wydajność. Oto, co mówią:

stos: ogólnie, nie zadzieraj z tym. Nawet go nie powiększaj. Wersja 8 oblicza ją tylko wtedy, gdy ktoś rzeczywiście czyta właściwość, co znacznie poprawia wydajność w przypadku błędów, które można rozwiązać. Jeśli przeczytasz właściwość tylko po to, aby ją powiększyć, poniesiesz koszty, nawet jeśli dzwoniący nie potrzebuje stosu.

Lubię i chciałbym wspomnieć ich pomijaniu oryginalnego błędu, który jest dobrym zamiennikiem przekazywania stosu.

Oto, w jaki sposób tworzę błąd niestandardowy, uwzględniając powyższe:

wersja es5:

function RError(options) {
    options = options || {}; // eslint-disable-line no-param-reassign
    this.name = options.name;
    this.message = options.message;
    this.cause = options.cause;

    // capture stack (this property is supposed to be treated as private)
    this._err = new Error();

    // create an iterable chain
    this.chain = this.cause ? [this].concat(this.cause.chain) : [this];
}
RError.prototype = Object.create(Error.prototype, {
    constructor: {
        value: RError,
        writable: true,
        configurable: true
    }
});

Object.defineProperty(RError.prototype, 'stack', {
    get: function stack() {
        return this.name + ': ' + this.message + '\n' + this._err.stack.split('\n').slice(2).join('\n');
    }
});

Object.defineProperty(RError.prototype, 'why', {
    get: function why() {
        var _why = this.name + ': ' + this.message;
        for (var i = 1; i < this.chain.length; i++) {
            var e = this.chain[i];
            _why += ' <- ' + e.name + ': ' + e.message;
        }
        return _why;
    }
});

// usage

function fail() {
    throw new RError({
        name: 'BAR',
        message: 'I messed up.'
    });
}

function failFurther() {
    try {
        fail();
    } catch (err) {
        throw new RError({
            name: 'FOO',
            message: 'Something went wrong.',
            cause: err
        });
    }
}

try {
    failFurther();
} catch (err) {
    console.error(err.why);
    console.error(err.stack);
    console.error(err.cause.stack);
}

wersja es6:

class RError extends Error {
    constructor({name, message, cause}) {
        super();
        this.name = name;
        this.message = message;
        this.cause = cause;
    }
    [Symbol.iterator]() {
        let current = this;
        let done = false;
        const iterator = {
            next() {
                const val = current;
                if (done) {
                    return { value: val, done: true };
                }
                current = current.cause;
                if (!val.cause) {
                    done = true;
                }
                return { value: val, done: false };
            }
        };
        return iterator;
    }
    get why() {
        let _why = '';
        for (const e of this) {
            _why += `${_why.length ? ' <- ' : ''}${e.name}: ${e.message}`;
        }
        return _why;
    }
}

// usage

function fail() {
    throw new RError({
        name: 'BAR',
        message: 'I messed up.'
    });
}

function failFurther() {
    try {
        fail();
    } catch (err) {
        throw new RError({
            name: 'FOO',
            message: 'Something went wrong.',
            cause: err
        });
    }
}

try {
    failFurther();
} catch (err) {
    console.error(err.why);
    console.error(err.stack);
    console.error(err.cause.stack);
}

Umieściłem swoje rozwiązanie w module, oto on: https://www.npmjs.com/package/rerror

borisdiakur
źródło
3

Lubię to robić w ten sposób:

  • Skorzystaj z nazwy, aby wyrzucić toString ()"{code}: {message}"
  • Zwróć tę samą rzecz super, aby wyglądała tak samo w stacktrace
  • Dołącz kod, error.codeponieważ sprawdzanie / parsowanie kodu jest lepsze w kodzie niż sprawdzanie wiadomości, którą możesz chcieć zlokalizować na przykład
  • Załącz wiadomość error.messagejako alternatywę dlaerror.toString()

class AppException extends Error {
  constructor(code, message) {
    const fullMsg = message ? `${code}: ${message}` : code;
    super(fullMsg);
    this.name = code;
    this.code = code;
    this.message = fullMsg;
  }
  
  toString() {
    return this.message;
  }
}

// Just a code
try {
  throw new AppException('FORBIDDEN');
} catch(e) {
  console.error(e);
  console.error(e.toString());
  console.log(e.code === 'FORBIDDEN');
}

// A code and a message
try {
  throw new AppException('FORBIDDEN', 'You don\'t have access to this page');
} catch(e) {
  console.error(e);
  console.error(e.toString());
  console.log(e.code === 'FORBIDDEN');
}

Dominik
źródło
2

Po prostu musiałem zaimplementować coś takiego i odkryłem, że stos został utracony w mojej własnej implementacji błędu. Musiałem stworzyć fałszywy błąd i pobrać z niego stos:

My.Error = function (message, innerException) {
    var err = new Error();
    this.stack = err.stack; // IMPORTANT!
    this.name = "Error";
    this.message = message;
    this.innerException = innerException;
}
My.Error.prototype = new Error();
My.Error.prototype.constructor = My.Error;
My.Error.prototype.toString = function (includeStackTrace) {
    var msg = this.message;
    var e = this.innerException;
    while (e) {
        msg += " The details are:\n" + e.message;
        e = e.innerException;
    }
    if (includeStackTrace) {
        msg += "\n\nStack Trace:\n\n" + this.stack;
    }
    return msg;
}
Jules
źródło
To nie ustawia wiadomości
BT
2

Użyłem Wzorca Konstruktora, aby utworzyć nowy obiekt błędu. Zdefiniowałem łańcuch prototypowy, taki jak Errorinstancja. Zobacz odniesienie do konstruktora błędów MDN .

Możesz sprawdzić ten fragment o tym GIST .

REALIZACJA

// Creates user-defined exceptions
var CustomError = (function() {
  'use strict';

  //constructor
  function CustomError() {
    //enforces 'new' instance
    if (!(this instanceof CustomError)) {
      return new CustomError(arguments);
    }
    var error,
      //handles the arguments object when is passed by enforcing a 'new' instance
      args = Array.apply(null, typeof arguments[0] === 'object' ? arguments[0] : arguments),
      message = args.shift() || 'An exception has occurred';

    //builds the message with multiple arguments
    if (~message.indexOf('}')) {
      args.forEach(function(arg, i) {
        message = message.replace(RegExp('\\{' + i + '}', 'g'), arg);
      });
    }

    //gets the exception stack
    error = new Error(message);
    //access to CustomError.prototype.name
    error.name = this.name;

    //set the properties of the instance
    //in order to resemble an Error instance
    Object.defineProperties(this, {
      stack: {
        enumerable: false,
        get: function() { return error.stack; }
      },
      message: {
        enumerable: false,
        value: message
      }
    });
  }

  // Creates the prototype and prevents the direct reference to Error.prototype;
  // Not used new Error() here because an exception would be raised here,
  // but we need to raise the exception when CustomError instance is created.
  CustomError.prototype = Object.create(Error.prototype, {
    //fixes the link to the constructor (ES5)
    constructor: setDescriptor(CustomError),
    name: setDescriptor('JSU Error')
  });

  function setDescriptor(value) {
    return {
      configurable: false,
      enumerable: false,
      writable: false,
      value: value
    };
  }

  //returns the constructor
  return CustomError;
}());

STOSOWANIE

Konstruktor CustomError może odbierać wiele argumentów do zbudowania komunikatu, np

var err1 = new CustomError("The url of file is required"),
    err2 = new CustomError("Invalid Date: {0}", +"date"),
    err3 = new CustomError("The length must be greater than {0}", 4),
    err4 = new CustomError("Properties .{0} and .{1} don't exist", "p1", "p2");

throw err4;

I tak wygląda błąd niestandardowy:

Niestandardowy łańcuch prototypów błędów

jherax
źródło
Ten, który przegłosował, czy masz argumenty lub powód do głosowania? lub po prostu nie rozumie intencji w kodzie.
jherax
Właśnie zauważyłem, że przypadkowo kliknąłem przycisk downvote podczas przeglądania tej strony, nie zdając sobie z tego sprawy (prawdopodobnie przeglądam z mojego telefonu). Dopiero dzisiaj zauważyłem przeglądając moją historię. To zdecydowanie nie było zamierzone, ale nie mogę tego cofnąć, ponieważ trwa to okres karencji. Podałeś pouczającą odpowiedź i zdecydowanie nie zasługujesz na to. Jeśli dokonasz edycji, chętnie cofnę głos głosowania. Przepraszam za to.
jschr
1

Konstruktor musi być jak metoda fabryczna i zwracać to, co chcesz. Jeśli potrzebujesz dodatkowych metod / właściwości, możesz dodać je do obiektu przed jego zwróceniem.

function NotImplementedError(message) { return new Error("Not implemented", message); }

x = new NotImplementedError();

Chociaż nie jestem pewien, dlaczego miałbyś to zrobić. Dlaczego nie po prostu użyć new Error...? Wyjątki niestandardowe tak naprawdę nie dodają wiele w JavaScript (ani prawdopodobnie w żadnym nietypowym języku).

pluckyglen
źródło
2
Musisz włączyć hierarchię typów błędów lub wartość obiektu w JavaScript, ponieważ możesz określić tylko jeden blok catch. W twoim rozwiązaniu (x instanceof NotImplementedError) ma wartość false, co w moim przypadku jest niedopuszczalne.
cdleary
1

Ładnie zaimplementowano to w Cesium DeveloperError:

W uproszczonej formie:

var NotImplementedError = function(message) {
    this.name = 'NotImplementedError';
    this.message = message;
    this.stack = (new Error()).stack;
}

// Later on...

throw new NotImplementedError();
Aram Kocharyan
źródło
Działa to świetnie, z tym wyjątkiem, że stos będzie zawierał dodatkową linię dla konstruktora błędów, co może stanowić problem.
SystemParadox
Ponadto nie przechodzi error instanceof Errortestu, co może być pomocne.
Lauren
1

To jest moja realizacja:

class HttpError extends Error {
  constructor(message, code = null, status = null, stack = null, name = null) {
    super();
    this.message = message;
    this.status = 500;

    this.name = name || this.constructor.name;
    this.code = code || `E_${this.name.toUpperCase()}`;
    this.stack = stack || null;
  }

  static fromObject(error) {
    if (error instanceof HttpError) {
      return error;
    }
    else {
      const { message, code, status, stack } = error;
      return new ServerError(message, code, status, stack, error.constructor.name);
    }
  }

  expose() {
    if (this instanceof ClientError) {
      return { ...this };
    }
    else {
      return {
        name: this.name,
        code: this.code,
        status: this.status,
      }
    }
  }
}

class ServerError extends HttpError {}

class ClientError extends HttpError { }

class IncorrectCredentials extends ClientError {
  constructor(...args) {
    super(...args);
    this.status = 400;
  }
}

class ResourceNotFound extends ClientError {
  constructor(...args) {
    super(...args);
    this.status = 404;
  }
}

Przykładowe użycie nr 1:

app.use((req, res, next) => {
  try {
    invalidFunction();
  }
  catch (err) {
    const error = HttpError.fromObject(err);
    return res.status(error.status).send(error.expose());
  }
});

Przykładowe użycie nr 2:

router.post('/api/auth', async (req, res) => {
  try {
    const isLogged = await User.logIn(req.body.username, req.body.password);

    if (!isLogged) {
      throw new IncorrectCredentials('Incorrect username or password');
    }
    else {
      return res.status(200).send({
        token,
      });
    }
  }
  catch (err) {
    const error = HttpError.fromObject(err);
    return res.status(error.status).send(error.expose());
  }
});
Rafał Enden
źródło
0

Kosztem niemożności użycia instanceof, poniższe zachowuje oryginalny ślad stosu i nie używa żadnych niestandardowych lew.

// the function itself
var fixError = function(err, name) {
    err.name = name;
    return err;
}

// using the function
try {
    throw fixError(new Error('custom error message'), 'CustomError');
} catch (e) {
    if (e.name == 'CustomError')
        console.log('Wee! Custom Error! Msg:', e.message);
    else
        throw e; // unhandled. let it propagate upwards the call stack
}
Gima
źródło
wszystko, co musisz tutaj zrobić, aby móc korzystać z instanceof, to rzucić nowy fixError zamiast po prostu fixError
BT
@BT: Nie z fixErrorpowyższą funkcją. Dodanie newpodczas wywołania spowoduje po prostu utworzenie obiektu, który zostanie wyrzucony.
TJ Crowder
Och, chyba miałam na myśli użycie „instanceof fixError” - oczywiście wtedy „instanceof Error” nie działałby. Myślę, że to gorzej ..
BT
0

Inna alternatywa, może nie działać we wszystkich środowiskach. Najpewniej zapewniam, że działa w nodejs 0.8 Podejście to wykorzystuje niestandardowy sposób modyfikowania wewnętrznego prototypu

function myError(msg){ 
      var e = new Error(msg); 
      _this = this; 
      _this.__proto__.__proto__ = e;
}
Chandu
źródło
0

Jeśli używasz Node / Chrome. Poniższy fragment kodu zapewni rozszerzenie spełniające następujące wymagania.

  • err instanceof Error
  • err instanceof CustomErrorType
  • Funkcja console.log () zwraca [CustomErrorType] po utworzeniu z komunikatem
  • console.log () zwraca, [CustomErrorType: message]gdy zostanie utworzony bez komunikatu
  • throw / stack udostępnia informacje w miejscu, w którym błąd został utworzony.
  • Działa optymalnie w Node.JS i Chrome.
  • Przejdzie kontrolę instancji w Chrome, Safari, Firefox i IE 8+, ale nie będzie mieć prawidłowego stosu poza Chrome / Safari. Nie przeszkadza mi to, ponieważ mogę debugować w chrome, ale kod wymagający określonych typów błędów nadal będzie działał w różnych przeglądarkach. Jeśli potrzebujesz tylko węzła, możesz łatwo usunąć ifwyciągi i możesz już iść .

Skrawek

var CustomErrorType = function(message) {
    if (Object.defineProperty) {
        Object.defineProperty(this, "message", {
            value : message || "",
            enumerable : false
        });
    } else {
        this.message = message;
    }

    if (Error.captureStackTrace) {
        Error.captureStackTrace(this, CustomErrorType);
    }
}

CustomErrorType.prototype = new Error();
CustomErrorType.prototype.name = "CustomErrorType";

Stosowanie

var err = new CustomErrorType("foo");

Wynik

var err = new CustomErrorType("foo");
console.log(err);
console.log(err.stack);

[CustomErrorType: foo]
CustomErrorType: foo
    at Object.<anonymous> (/errorTest.js:27:12)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:906:3

/errorTest.js:30
        throw err;
              ^
CustomErrorType: foo
    at Object.<anonymous> (/errorTest.js:27:12)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:906:3
Nukleon
źródło
0

Poniższe działało dla mnie zaczerpnięte z oficjalnego błędu dokumentacji Mozilli .

function NotImplementedError(message) {
    var instance = new Error(message);
    instance.name = 'NotImplementedError';

    Object.setPrototypeOf(instance, Object.getPrototypeOf(this));
    if (Error.captureStackTrace) {
        Error.captureStackTrace(instance, NotImplementedError);
    }
    return instance;
}

NotImplementedError.prototype = Object.create(Error.prototype, {
    constructor: {
        value: Error,
        enumerable: false,
        writable: true,
        configurable: true
    }
});
Zhunio
źródło
-1

Wypróbuj nowy obiekt prototypowy dla każdej instancji typu błędu zdefiniowanego przez użytkownika. Pozwala instanceofto zachowywać się jak zwykle, a typ i komunikat są poprawnie zgłaszane w Firefox i V8 (Chome, nodejs).

function NotImplementedError(message){
    if(NotImplementedError.innercall===undefined){
        NotImplementedError.innercall = true;
        NotImplementedError.prototype = new Error(message);
        NotImplementedError.prototype.name = "NotImplementedError";
        NotImplementedError.prototype.constructor = NotImplementedError;

        return new NotImplementedError(message);
    }
    delete NotImplementedError.innercall;
}

Zauważ, że dodatkowy wpis poprzedzi prawidłowy stos.

Augustus Kling
źródło
Nie działa Spróbuj: var a = new NotImplementedError('a'), b = new NotImplementedError('b');. Teraz a instanceof NotImplementedError == falseib instanceof NotImplementedError == true
jjrv
-1

To najszybszy sposób na zrobienie tego:

    let thisVar = false

    if (thisVar === false) {
            throw new Error("thisVar is false. It should be true.")
    }
jlhs
źródło
-3

łatwiejszy sposób. Możesz sprawić, że Twój obiekt odziedziczy po obiekcie Error. Przykład:

function NotImplementError(message)
{
    this.message = message;
    Error.call();
    Error.call(message);
} 

używamy funkcji call (), która wywołuje konstruktor klasy Error, więc jest to w zasadzie to samo, co implementacja dziedziczenia klas w innych językach obiektowych.

Paulo Enmanuel
źródło
-3

MDN ma doskonały przykład :

try {
  throw new Error('Whoops!');
} catch (e) {
  console.log(e.name + ': ' + e.message);
}
Ronnie Royston
źródło