web-dev-qa-db-fra.com

Exceptions personnalisées en JavaScript

Puis-je définir des types personnalisés pour les exceptions définies par l'utilisateur dans JavaScript? Si je peux, comment pourrais-je le faire?

215
Manki

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:[email protected]\">system administrator</a>.",
  toString:    function(){return this.name + ": " + this.message;} 
}; 
223
jon077

Vous devez créer une exception personnalisée qui hérite de manière prototypique de Error. Par exemple:

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;

Ceci est fondamentalement une version simplifiée de ce que disfated a posté ci-dessus avec l'amélioration qui permet aux traces de pile de fonctionner sur Firefox et d'autres navigateurs. Il satisfait aux mêmes tests qu'il a postés:

Usage:

throw new InvalidArgumentException();
var err = new InvalidArgumentException("Not yet...");

Et on se comportera comme prévu:

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!
84
asselin

Vous pouvez implémenter vos propres exceptions et leur traitement, par exemple comme ici:

// 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");
    }
}

Il existe une autre syntaxe pour intercepter une exception typée, bien que cela ne fonctionne pas dans tous les navigateurs (par exemple, pas dans 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");
}
79
Sergey Ilinsky

Oui. Vous pouvez lancer ce que vous voulez: des entiers, des chaînes de caractères, des objets, peu importe. Si vous souhaitez lancer un objet, créez simplement un nouvel objet, comme vous le feriez dans d'autres circonstances, puis jetez-le. référence Javascript de Mozilla a plusieurs exemples.

39
Rob Kennedy
function MyError(message) {
 this.message = message;
}

MyError.prototype = new Error;

Cela permet une utilisation comme ..

try {
  something();
 } catch(e) {
  if(e instanceof MyError)
   doSomethingElse();
  else if(e instanceof Error)
   andNowForSomethingCompletelyDifferent();
}
26
Morgan ARR Allen

En bref:

Option 1: utilisez babel-plugin-transform-builtin-extend

Option 2: faites-le vous-même (inspiré de cette même bibliothèque)

    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 vous utilisez pur ES5 :

    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;
    }
    
  • Alternative: utilisation Classtrophobic cadre

Explication:

Pourquoi étendre la classe Error en utilisant ES6 et Babel est un problème?

Car une instance de CustomError n'est plus reconnue en tant que telle.

class CustomError extends Error {}
console.log(new CustomError('test') instanceof Error);// true
console.log(new CustomError('test') instanceof CustomError);// false

En fait, dans la documentation officielle de Babel, vous ne pouvez pas étendre les classes JavaScript intégrées tel que Date, Array, DOM ou Error.

Le problème est décrit ici:

Qu'en est-il de l'autre SO répond?

Toutes les réponses données résolvent le problème instanceof mais vous perdez l’erreur habituelle console.log:

console.log(new CustomError('test'));
// output:
// CustomError {name: "MyError", message: "test", stack: "Error↵    at CustomError (<anonymous>:4:19)↵    at <anonymous>:1:5"}

Alors que vous utilisez la méthode mentionnée ci-dessus, non seulement vous corrigez le problème instanceof mais vous conservez l’erreur habituelle console.log:

console.log(new CustomError('test'));
// output:
// Error: test
//     at CustomError (<anonymous>:2:32)
//     at <anonymous>:1:5
12
JBE

J'utilise souvent une approche avec l'héritage prototype. Remplacer toString() vous donne l’avantage que des outils tels que Firebug enregistrent les informations réelles au lieu de [object Object] à la console pour les exceptions non interceptées.

Utilisez instanceof pour déterminer le type d’exception.

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();
11
Matthias

Voici comment vous pouvez créer des erreurs personnalisées avec un comportement complètement identique au comportement natif de Error. Cette technique fonctionne seulement dans Chrome et node.js pour le moment. J'ai aussi ne recommanderais pas de l'utiliser si vous ne le faites pas comprendre ce qu'il fait.

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;
    }

})();

Comme résultat nous obtenons

/**
 * 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)

Ensuite, vous pouvez l'utiliser comme ceci:

var NotImplementedError = Error.createCustromConstructor('NotImplementedError');

Et utilisez NotImplementedError comme vous le feriez Error:

throw new NotImplementedError();
var err = new NotImplementedError();
var err = NotImplementedError('Not yet...');

Et on se comportera comme prévu:

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!

Notez que error.stack Fonctionne correctement et n'inclut pas NotImplementedError appel de constructeur (grâce à la v8 de Error.captureStackTrace()).

Remarque. Il y a laid eval(). La seule raison pour laquelle il est utilisé est d'obtenir le correct err.constructor.name. Si vous n'en avez pas besoin, vous pouvez tout simplifier un peu.

11
disfated

Utilisez l'instruction throw .

JavaScript ne s'intéresse pas au type d'exception (comme Java le fait). JavaScript remarque juste qu'il y a une exception et quand vous l'interprétez, vous pouvez "regarder" ce que l'exception "dit".

Si vous devez générer différents types d'exceptions, nous vous suggérons d'utiliser des variables contenant la chaîne/l'objet de l'exception, c'est-à-dire le message. Si vous en avez besoin, utilisez "throw myException" et dans la capture, comparez l'exception capturée à myException.

5
Xn0vv3r

ES6

Avec la nouvelle classe et les mots-clés d’extension, c’est maintenant beaucoup plus facile:

class CustomError extends Error {
  constructor(message) {
    super(message);
    //something
  }
}
4
Brunno

Voir cet exemple dans le MDN.

Si vous devez définir plusieurs erreurs (testez le code ici !):

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;

Le code est principalement copié à partir de: Quel est un bon moyen d'étendre Error in JavaScript?

1
Peter Tseng

Une alternative à la réponse de asselin à utiliser avec les classes ES2015

class InvalidArgumentException extends Error {
    constructor(message) {
        super();
        Error.captureStackTrace(this, this.constructor);
        this.name = "InvalidArgumentException";
        this.message = message;
    }
}
1
Mr Tsjolder
//create error object
var error = new Object();
error.reason="some reason!";

//business function
function exception(){
    try{
        throw error;
    }catch(err){
        err.reason;
    }
}

Nous allons maintenant ajouter la raison ou les propriétés que nous voulons à l'objet d'erreur et le récupérer. En rendant l'erreur plus raisonnable.

1
Mateen