カスタム例外タイプ


224

JavaScriptでユーザー定義の例外のカスタムタイプを定義できますか?もしそうなら、どうすればいいですか?


3
注意してください。JavaScriptの10分によると、ボックス化されていない値をスローした場合、スタックトレースは取得されません。
Janus Troelsen、2011

exceptionsjs.comは、カスタム例外を作成する機能を提供し、デフォルトでArgumentExceptionやNotImplementedを含むいくつかの不足している例外を提供します。
Steven Wexler、2014

回答:


232

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

7
@ b.long「JavaScript:The Good Parts」(素晴らしい本IMO)にあります。このGoogleブックスのプレビューでは、books.google.com
books?id

11
toStringメソッドを追加すると、JavaScriptコンソールに適切に表示されます。次のように表示されない場合:キャッチされない#<Object> toStringが表示される場合:キャッチされないシステムエラー:エラーが検出されました。システム管理者に連絡してください。
JDC 2013

11
エラーから継承しない限り、これはスタックトレースを許可しません
ルークH 14

このカスタムエラーでのみ機能するように、catchブロック内でどのようにフィルタリングできますか?
Overdrivr

over catch (e) { if (e instanceof TypeError) { … } else { throw e; } }driや⦃⦄などのcatch (e) { switch (e.constructor) { case TypeError: …; break; default: throw e; }@overdrivr。
sam boosalis

92

Errorからプロトタイプ的に継承するカスタム例外を作成する必要があります。例えば:

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;

これは、基本的には何の簡易版であるdisfatedスタックトレースは、Firefoxや他のブラウザ上で動作することを強化して上に投稿しました。それは彼が投稿したのと同じテストを満たしています:

使用法:

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

そして、それは期待される動作になります:

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!

80

独自の例外とその処理を、たとえば次のように実装できます。

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

型付きの例外をキャッチするための別の構文がありますが、これはすべてのブラウザー(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");
}

2
MSN Webサイトでは、条件のキャッチについて次の警告が表示されます。非標準この機能は非標準であり、標準化されていません。Webに面している本番サイトでは使用しないでください。すべてのユーザーに対して機能するわけではありません。また、実装間に大きな非互換性があり、動作が将来変更される可能性があります。
Lawrence Dol

40

はい。整数、文字列、オブジェクトなど、好きなものを投げることができます。オブジェクトをスローする場合は、他の状況でオブジェクトを作成するのと同じように、新しいオブジェクトを作成してからスローします。MozillaのJavaScriptリファレンスにはいくつかの例があります。


26
function MyError(message) {
 this.message = message;
}

MyError.prototype = new Error;

これにより、次のような使用が可能になります。

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

Errorのプロトタイプを継承していなくても、この簡単な例はまったく同じように動作しませんか?この例で何が得られるかははっきりしません。
EleventyOne 2013

1
いいえ、e instanceof Error誤りです。
モーガンARRアレン

確かに。しかし、e instanceof MyError真実であるため、else if(e instanceof Error)ステートメントは決して評価されません。
EleventyOne 2013

右、これは、このスタイルのtry / catchがどのように機能するかの例にすぎません。どこelse if(e instanceof Error)が最後の問題でしょう。おそらく単純なものが続きますelse(これは含めませんでした)。default:スイッチステートメントのようなものですが、エラーです。
モーガンARRアレン

15

要するに:

  • トランスパイラーなしで ES6 使用している場合:

    class CustomError extends Error { /* ... */}

    現在のベストプラクティスについては、ES6構文を使用したJavaScriptでのエラーの拡張をご覧ください。

  • Babelトランスパイラーを使用している場合:

オプション1:babel-plugin-transform-b​​uiltin-extendを使用する

オプション2:自分でやる(同じライブラリから着想を得た)

    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);
  • 純粋な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;
    }
  • 代替:Classtrophobicフレームワークを使用する

説明:

ES6とBabelを使用してErrorクラスを拡張することが問題なのはなぜですか?

CustomErrorのインスタンスがそのように認識されなくなったためです。

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

実際には、バベルの公式ドキュメントから、あなたはどのビルトインのJavaScriptクラス拡張することはできませんようにDateArrayDOMまたはError

問題はここに記述されています:

他のSOの答えはどうですか?

与えられた答えはすべてinstanceof問題を修正しますが、通常のエラーを失いますconsole.log

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

上記の方法を使用する一方で、instanceof問題を修正するだけでなく、通常のエラーも保持しますconsole.log

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

11

これが、ネイティブErrorの動作とまったく同じカスタムエラーを作成する方法です。この手法は、現時点ではChromeとnode.jsのみ機能します。また、それが何をするものか理解していないのなら、それを使うことはお勧めしません。

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

})();

結果として、

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

その後、次のように使用できます。

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

そしてNotImplementedError、あなたがするように使用してくださいError

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

そして、それは期待される動作になります:

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!

これerror.stackは絶対に機能し、NotImplementedErrorコンストラクター呼び出しは含まれません(v8のおかげでError.captureStackTrace())。

注意。醜いeval()です。それが使用される唯一の理由は、正しく取得することですerr.constructor.name。あなたがそれを必要としないならば、あなたは少しすべてを単純化することができます。


2
Error.apply(self, arguments)されていない仕事に指定されました代わりに、ブラウザー間で互換性のあるスタックトレースをコピーすることをお勧めします
Kornel

11

私はよくプロトタイプ継承のアプローチを使用します。オーバーライドtoString()すると、Firebugなどのツールではなく、実際の情報がログに記録されるという利点があります[object Object]、キャッチされなかった例外のコンソールではあります。

使用する instanceof例外のタイプを判別ためにします。

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

8

ES6

新しいクラスと拡張キーワードを使用すると、はるかに簡単になりました。

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

7

投げを使うステートメントをます。

JavaScriptは、(Javaのように)例外タイプが何であるかを気にしません。JavaScriptは単に気づくだけで、例外があり、それをキャッチすると、例外が「何を言っているか」を「見る」ことができます。

スローしなければならない例外の種類が異なる場合は、例外の文字列/オブジェクト(メッセージ)を含む変数を使用することをお勧めします。必要な場合は、「throw myException」を使用し、catchで、キャッチされた例外をmyExceptionと比較します。


1

MDNでこの例を参照してください。

複数のエラーを定義する必要がある場合(ここでコードをテストしください):

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;

コードは主に以下からコピーされます:JavaScriptでエラーを拡張する良い方法は何ですか?


1

ES2015クラスで使用するためのasselinの回答の代替

class InvalidArgumentException extends Error {
    constructor(message) {
        super();
        Error.captureStackTrace(this, this.constructor);
        this.name = "InvalidArgumentException";
        this.message = message;
    }
}

1
//create error object
var error = new Object();
error.reason="some reason!";

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

次に、理由または必要なプロパティをエラーオブジェクトに追加して取得します。エラーをより合理的にすることによって。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.