JavaScriptでエラーを拡張する良い方法は何ですか?


369

私はJSコードでいくつかのものをスローしたいし、それらをinstanceof Errorにしたいが、それらを別のものにしたい。

Pythonでは、通常、Exceptionをサブクラス化します。

JSで適切なことは何ですか?

回答:


218

Errorオブジェクトが持つ唯一の標準フィールドはmessageプロパティです。(MDNまたはEcmaScript言語仕様のセクション15.11を参照)その他はすべてプラットフォーム固有です。

MOST環境は設定stackプロパティが、fileNameそしてlineNumber相続で使用することが事実上役に立ちません。

したがって、最小限のアプローチは次のとおりです。

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

スタックをスニッフィングし、不要な要素をシフト解除して、fileNameやlineNumberなどの情報を抽出できますが、そのためには、JavaScriptが現在実行されているプラ​​ットフォームに関する情報が必要です。これはほとんどの場合不要です。本当に必要な場合は、事後的に行うことができます。

Safariは注目すべき例外です。stackプロパティはありませんが、スローされているオブジェクトのthrowキーワードセットsourceURLlineプロパティがあります。それらは正しいことが保証されています。

私が使用したテストケースはここにあります:JavaScript自作エラーオブジェクト比較


19
this.name = 'MyError' 関数の外側を移動して、に変更できMyError.prototype.name = 'MyError'ます。
ダニエルビアズリー

36
これが唯一の正しい答えですが、スタイルの問題として、おそらくこのように記述します。function MyError(message) { this.message = message; this.stack = Error().stack; } MyError.prototype = Object.create(Error.prototype); MyError.prototype.name = "MyError";
kybernetikos

11
MyError.prototype.constructor = MyErrorも追加します。
Bharat Khatri 14

3
ES6のError.call(this、message); 初期化する必要がありますthisか?
4esn0k 2014

4
MyError.prototype = Object.create(Error.prototype);
鳥のようなロビン

170

ES6の場合:

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

ソース


53
サブクラスはクラスを拡張する必要があるため、Babelなどのトランスパイラーを介してES6機能を使用している場合、これは機能しないことに言及する価値があります。
aaaidan 2016年

6
あなたはバベルを使用して、ノード> 5.xのあなたはes2015プリセットを使用しますがすべきではないにしているしている場合npmjs.com/package/babel-preset-node5拡張に加えて、よりネイティブES6を使用することができるようになる
エース

2
これは、可能な場合にこれを行うための最良の方法です。カスタムエラーは、ChromeとFirefoxの両方で(そしておそらく他のブラウザーでも)通常のエラーのように動作します。
Matt Browne

2
ブラウザの場合、実行時にクラスのサポートを検出し、それに応じて非クラスバージョンにフォールバックできることに注意してください。検出コード:var supportsClasses = false; try {eval('class X{}'); supportsClasses = true;} catch (e) {}
Matt Browne

31
の保守を容易にするためにthis.name = this.constructor.name;代わりに使用してください。
КонстантинВан

53

要するに:

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

    class CustomError extends Error { /* ... */}
  • 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) {
      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;
    }
  • 代替: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

1
class CustomError extends Error { /* ... */}はベンダー固有の引数(lineNumberなど)を正しく処理しません。「ES6構文を使用したJavascriptの拡張エラー」はBabel固有であり、ES5ソリューションが使用しconst、カスタム引数を処理しません。
Indolering

非常に完全な答え。
Daniele Orlando

これは実際に最も包括的なソリューションを提供し、さまざまな部分が必要な理由を説明します。どうもありがとうJBE!
AndrewSouthpaw

これは、「エラー」からの継承に関する問題を解決するのに役立ちました。2時間の悪夢だった!
1930

2
の問題console.log(new CustomError('test') instanceof CustomError);// falseは執筆時点では真実でしたが、現在は解決されていることに注目してください。実際にはその答えにリンクされ問題が解決されており、我々は正しい動作をテストすることができ、こことのコード貼り付けて、REPLをし、それが正しく、正しいプロトタイプチェーンをインスタンス化するtranspiled取得する方法を見て。
のび太

44

編集:コメントを読んでください。これはV8(Chrome / Node.JS)でのみ正常に機能することがわかりました。私の意図は、すべてのブラウザーで機能するクロスブラウザーソリューションを提供し、サポートがある場所でスタックトレースを提供することでした。

編集:さらに編集できるように、このコミュニティWikiを作成しました。

V8のソリューション(Chrome / Node.JS)、Firefoxで動作し、IEでほぼ正しく機能するように変更できます。(投稿の終わりを参照)

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
}

「コードを見せて!」に関する元の投稿

短縮版:

function UserError(message) {
  this.constructor.prototype.__proto__ = Error.prototype
  Error.captureStackTrace(this, this.constructor)
  this.name = this.constructor.name
  this.message = message
}

私はthis.constructor.prototype.__proto__ = Error.prototypeすべてのコードを一緒に保つために関数の中に保ちます。しかし、あなたはまた、置き換えることができthis.constructorUserError、それはそれは一度だけ呼び出されるように、あなたは、関数の外にコードを移動することができます。

そのルートに行く場合は、最初に投げる前に必ずそのライン電話してくださいUserError

順序に関係なく関数が最初に作成されるため、この警告は関数を適用しません。したがって、問題なく関数をファイルの最後に移動できます。

ブラウザの互換性

FirefoxとChrome(およびNode.JS)で動作し、すべての約束を満たします。

Internet Explorerが以下で失敗する

  • エラーはerr.stack最初からである必要はないので、「それは私のせいではありません」。

  • Error.captureStackTrace(this, this.constructor) 存在しないので、他のようなことをする必要があります

    if(Error.captureStackTrace) // AKA if not IE
        Error.captureStackTrace(this, this.constructor)
  • toStringをサブクラス化すると、は存在しなくなりますError。したがって、追加する必要もあります。

    else
        this.toString = function () { return this.name + ': ' + this.message }
  • IEは考慮されませんUserErrorようにinstanceof Errorあなたがあなたの前に、以下のいくつかの時間を実行しない限り、throw UserError

    UserError.prototype = Error.prototype

16
Firefoxが実際にcaptureStackTraceを持っているとは思いません。これはV8拡張機能であり、Firefoxで定義されていません。また、それをサポートするFirefoxへの参照をWeb上で見つけることもできません。(ありがとうございます!)
Geoff

5
Error.call(this)変更するのではなくエラーを返すため、実際には何もしていませんthis
kybernetikos

1
Node.jsに最適
Rudolf Meijering

1
UserError.prototype = Error.prototype誤解を招くです。これは継承を行わないため、同じクラスになります。
Halcyon 2013年

1
少なくとも現在のブラウザでObject.setPrototypeOf(this.constructor.prototype, Error.prototype)は、がに優先されると思いthis.constructor.prototype.__proto__ = Error.prototypeます。
ChrisV 2015年

29

エラーの種類ごとに定型回避するために、いくつかの解決策の知恵をcreateErrorType関数にまとめました  。

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

次に、次のように新しいエラータイプを簡単に定義できます

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;か?
Peter Tseng 2016

@PeterTseng nameはすでにプロトタイプに設定されているので、もう必要ありません。削除しました。ありがとう!
Ruben Verborgh、2016

27

では2018年、私はこれが最善の方法だと思います。IE9 +と最新のブラウザをサポートしています。

更新:異なる実装の比較については、このテストリポジトリを参照してください。

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

また、__proto__プロパティが、他の回答で広く使用さ非推奨である。


1
なぜ使用していsetPrototypeOf()ますか?少なくともMDNによれば.prototype、コンストラクターでプロパティを設定するだけで同じことを達成できる場合は、それを使用しないことをお勧めします(elseを持たない参照用のブロックで行っているようにsetPrototypeOf)。
Matt Browne

オブジェクトのプロトタイプを変更することはお勧めできませんsetPrototypeOf。ただし、それでも必要な場合は(OPからの質問)、組み込みの方法論を使用する必要があります。MDNが示すように、これはオブジェクトのプロトタイプを設定する適切な方法と考えられています。つまり、MDNはプロトタイプを変更しないでください(パフォーマンスと最適化に影響するため)が、必要な場合はを使用しますsetPrototypeOf
OnurYıldırım2016

ここでプロトタイプを実際に変更する必要はないと思います。下の行(CustomError.prototype = Object.create(Error.prototype))を使用するだけです。また、のObject.setPrototypeOf(CustomError, Error.prototype)新しいインスタンスのプロトタイプを指定するのではなく、コンストラクタ自体のプロトタイプを設定していますCustomError。とにかく、2016年に私は、私はまだバベルと一緒にそれを使用する方法を考え出すていますが、エラーを拡張するためのより良い方法は、実際にあると思う:github.com/loganfsmyth/babel-plugin-transform-b​​uiltin-extend/...
マット・ブラウン

CustomError.prototype = Object.create(Error.prototype)プロトタイプも変更しています。ES5には組み込みの拡張/継承ロジックがないため、これを変更する必要があります。あなたが言及しているbabelプラグインも同様のことをしていると思います。
OnurYıldırım2016

1
Object.setPrototypeOf少なくともここでは使用する意味がない理由を説明する要点を作成しました:gist.github.com/mbrowne/4af54767dcb3d529648f5a8aa11d6348。おそらくあなたは書くObject.setPrototypeOf(CustomError.prototype, Error.prototype)つもりでした-それはもう少し理にかなっています(ただし、を設定するだけの利点はありませんCustomError.prototype)。
Matt Browne

19

完全を期すために-以前の回答でこのメソッドについて言及していなかったという理由だけで-Node.jsを使用していて、ブラウザーの互換性を気にする必要がない場合は、組み込みで目的の効果を簡単に達成できますモジュール(ここでは公式ドキュメント)。inheritsutil

たとえば、最初の引数としてエラーコードを受け取り、2番目の引数としてエラーメッセージを受け取るカスタムエラークラスを作成するとします。

ファイル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;

これで、インスタンスを作成してパス/スローすることができます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!');

このスニペットを使用すると、スタックトレースには正しいファイル名と行が含まれ、エラーインスタンスには正しい名前が付けられます。

これは、ターゲットオブジェクト(この場合は、インスタンス化される)にプロパティcaptureStackTraceを作成するメソッドの使用が原因で発生します。動作の詳細については、こちらのドキュメントをご覧くださいstackCustomError


1
this.message = this.message;これは間違っているのですか、それともJSについて知らないクレイジーなことがまだありますか?
アレックス

1
@Alexさん、こんにちは。現在は修正されています。ありがとう!
VictorSchröder2017

18

クレセントフレッシュの回答の投票数の多い回答は誤解を招くものです。彼の警告は無効ですが、彼が対処していない他の制限があります。

第一に、クレセントの「警告」の段落の論法は意味をなさない。説明は、「if(error instance of MyError)else ...」のコーディングが、複数のcatchステートメントに比べて、いくぶん厄介または冗長であることを示しています。単一のcatchブロック内の複数のinstanceofステートメントは、複数のcatchステートメントと同じくらい簡潔です-トリックのないクリーンで簡潔なコード。これは、Javaの優れたスロー可能サブタイプ固有のエラー処理をエミュレートする優れた方法です。

WRTは「サブクラスのメッセージプロパティが表示されないように見えます」。これは、正しく作成されたエラーサブクラスを使用する場合には当てはまりません。独自のErrorX Errorサブクラスを作成するには、「var MyError = "で始まるコードブロックをコピーし、「MyError」という1つの単語を「ErrorX」に変更します。(サブクラスにカスタムメソッドを追加する場合は、サンプルテキストに従ってください)。

JavaScriptエラーサブクラス化の実際の重要な制限は、Firefoxのように、スタックトレースとインスタンス化の場所を追跡および報告するJavaScript実装またはデバッガーの場合、独自のエラーサブクラス実装の場所がインスタンス化ポイントとして記録されることです。一方、直接エラーを使用した場合は、「new Error(...)」を実行した場所になります)。IEユーザーはおそらく気付かないでしょうが、FFでのFire Bugのユーザーは、これらのエラーと共にレポートされる無用のファイル名と行番号の値を確認し、スタックトレースを要素#1までドリルダウンして、実際のインスタンス化の場所を見つける必要があります。


私はそれを正しく理解しましたか?サブクラス化せずに新しいError(...)を直接使用すると、ファイル名と行が適切に報告されますか?そして、あなたは基本的にエラーのサブクラス化(実際のセクシーで装飾的なものだけでなく)のサブクラス化エラーについて、意味がないと言いますか?
jayarjo

6
この回答は、Crescent Fresh's削除されたので混乱を招きます。
Peter

これはまだ事実ですか?developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 新しい番号が呼び出された場所では行番号が2ではありません
Muhammad Umer

13

このソリューションはどうですか?

以下を使用してカスタムエラーをスローする代わりに:

throw new MyError("Oops!");

Errorオブジェクトをラップします(デコレータのようなものです)。

throw new MyError(Error("Oops!"));

これにより、スタック、fileName lineNumberなどのすべての属性が正しいことを確認します。

あとは、属性をコピーするか、属性のゲッターを定義するだけです。getter(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();
};

1
このソリューションをnpmパッケージとしてリリースしました:npmjs.com/package/throwable
JoWie

信じられないほどエレガントなソリューション、共有してくれてありがとう!1つのバリエーション:最後の引数のプロパティを割り当てるためnew MyErr (arg1, arg2, new Error())に使用Object.assignするMyErrコンストラクターthis
Peeyush Kushwaha

私はこれが好き。継承の代わりにカプセル化を使用して、制限を回避します。
ジェニーオライリー

13

一部の人々が言っ​​たように、それはES6でかなり簡単です:

class CustomError extends Error { }

だから私は自分のアプリ(Angular、Typescript)でそれを試してみましたが、うまくいきませんでした。しばらくして、問題がTypescript:Oに起因していることがわかりました

見る https://github.com/Microsoft/TypeScript/issues/13965をください

あなたがそうするならば、それは非常に不安です:

class CustomError extends Error {}


try {
  throw new CustomError()
} catch(e) {
  if (e instanceof CustomError) {
    console.log('Custom error');
  } else {
    console.log('Basic error');
  }
}

ノードまたはブラウザに直接表示されます。 Custom error

TypescriptプレイグラウンドのプロジェクトのTypescriptでそれを実行しようとすると、表示されます Basic errorます...

解決策は次のとおりです。

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

10

私の解決策は、提供されている他の回答よりも単純であり、欠点はありません。

ErrorプロトタイプチェーンとErrorのすべてのプロパティを、それらの特定の知識がなくても保持します。Chrome、Firefox、Node、IE11でテストされています。

唯一の制限は、呼び出しスタックの最上部にある追加のエントリです。しかし、それは簡単に無視されます。

2つのカスタムパラメータを使用した例を次に示します。

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

使用例:

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
}

setPrototypeOfのポリフィルが必要な環境の場合:

Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
    obj.__proto__ = proto;
    return obj;
};

1
私の回答に記載されているように、このソリューションは、コンソールのスタックトレースの最初の行のみをログに記録するFirefoxまたはその他のブラウザーで問題を引き起こす可能性があります
Jonathan Benn

これは、ES5で適切に機能することがわかった唯一の回答です(ES6クラスを使用しても適切に機能します)。エラーは、Chromium DevToolsで他の回答よりもはるかにうまく表示されます。
Ben Gubler

注:TypeScriptでこのソリューションを使用している場合は、throw CustomError('err')代わりに次を実行する必要がありますthrow new CustomError('err')
Ben Gubler

8

上記の例ではError.applyError.call私も)何もしません(Firefox 3.6 / Chrome 5)。私が使用する回避策は次のとおりです。

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

5

他の人が言ったようにノードでは、それは簡単です:

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

3

私は他の人がすでに述べたことに追加したいだけです:

カスタムエラークラスがスタックトレースに正しく表示されるようにするには、カスタムエラークラスのプロトタイプのnameプロパティをカスタムエラークラスのnameプロパティに設定する必要があります。これは私が意味することです:

CustomError.prototype = Error.prototype;
CustomError.prototype.name = 'CustomError';

したがって、完全な例は次のようになります。

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

すべての処理が完了すると、新しい例外がスローされ、次のようになります(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)

5
これは、すべてのエラーインスタンスの名前プロパティを上書きしませんか?
panzi 14年

@panziあなたは正しいです。小さなバグを修正しました。ヘッドアップをありがとう!
Gautham C. 2014

3

私の2セント:

なぜ別の答えですか?

a)Error.stack(一部の回答のように)プロパティにアクセスするとパフォーマンスが大幅に低下するため。

b)1行しかないため。

c)https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Errorのソリューションはスタック情報を保持していないようです。

//MyError class constructor
function MyError(msg){
    this.__proto__.__proto__ = Error.apply(null, arguments);
};

使用例

http://jsfiddle.net/luciotato/xXyeB/

それは何をするためのものか?

this.__proto__.__proto__is MyError.prototype.__proto__なので__proto__、MyErrorのFOR ALL INSTANCESを新しく作成された特定のエラーに設定しています。MyErrorクラスのプロパティとメソッドを保持し、新しいErrorプロパティ(.stackを含む)も__proto__チェーンに入れます。

明らかな問題:

有用なスタック情報を持つMyErrorのインスタンスを複数持つことはできません。

何をするのか完全に理解してthis.__proto__.__proto__=いない場合は、このソリューションを使用しないでください。


2

JavaScriptの例外はサブクラス化が難しいため、サブクラス化しません。新しい例外クラスを作成し、その内部でエラーを使用します。Error.nameプロパティを変更して、コンソールのカスタム例外のように見せます。

var InvalidInputError = function(message) {
    var error = new Error(message);
    error.name = 'InvalidInputError';
    return error;
};

上記の新しい例外は通常のエラーと同じようにスローされ、期待どおりに機能します。次に例を示します。

throw new InvalidInputError("Input must be a string");
// Output: Uncaught InvalidInputError: Input must be a string 

警告:スタックトレースは完全ではありません。スタックトレースは、新しいエラーが作成された場所に移動し、スローした場所には移動しません。これは、コンソールで直接完全なスタックトレースを提供するため、Chromeでは大した問題ではありません。しかし、たとえば、Firefoxの方が問題になります。


これはケースで失敗しますm = new InvalidInputError(); dontThrowMeYet(m);
Eric

@エリック同意しますが、これはかなり小さな制限のようです。例外オブジェクトを事前にインスタンス化する必要はありませんでした(上のコードサンプルのようなメタプログラミングの使用を除いて)。これは本当にあなたにとって問題ですか?
ジョナサンベン

はい、動作は同じようですので、答えを変更します。スタックトレースに100%満足していません。これにより、FirefoxとChromeの「var error」行が表示されます
Jonathan Benn

1
@JonathanBenn私はパーティーに本当に遅れているので、多分あなたはすでにこれを拾ったでしょう。非同期プログラミングとPromiseを使用する場合、例外オブジェクトを頻繁にインスタンス化します。エリックの名前@に続いて、私は頻繁に使用しm = new ...、その後Promise.reject(m)。必要はありませんが、コードは読みやすくなっています。
BaldEagle

1
@JonathanBenn:(彼は)10月14日、例外オブジェクトをスローする前にインスタンス化することはまれだと考えていたようです。一度やってみた例を挙げました。それは一般的だとは言いませんが、必要なときに持っておくと便利です。また、インスタンス化はすべて1行で行われ、拒否はすべて別の行で行われるため、コードが読みやすくなります。私はそれがうまくいくことを願っています!
BaldEagle 2016

2

Mohsenの回答で指摘されているように、ES6では、クラスを使用してエラーを拡張することが可能です。ES6より前のブラウザーをサポートする必要がある場合は、ブラウザーでこれを使用するのは簡単です。それがどのように実装されるかについてのいくつかの注記については以下を参照してくださいが、それまでの間、他の回答からの最良の提案のいくつかを組み込んだ比較的単純なアプローチを提案します:

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

ES6では、次のように簡単です。

class CustomError extends Error {}

... ES6クラスのサポートをで検出try {eval('class X{}')できますが、古いブラウザで読み込まれたスクリプトにES6バージョンを含めようとすると、構文エラーが発生します。したがって、すべてのブラウザーをサポートする唯一の方法は、eval()ES6をサポートするブラウザー用に個別のスクリプトを動的に(たとえば、AJAXまたはを介して)ロードすることです。さらに複雑なのは、eval()(コンテンツセキュリティポリシーにより)すべての環境でサポートされているわけではないことです。これは、プロジェクトで考慮される場合とされない場合があります。

したがって、現時点では、上記の最初のアプローチ、またはError拡張を試みずに直接使用することが、非ES6ブラウザーをサポートする必要があるコードに対して実際に実行できる最善の方法のようです。

一部の人々が検討したいと思う他のアプローチが1つありObject.setPrototypeOf()ます。それは、カスタムエラータイプのインスタンスであるが、コンソールのネイティブエラーのように見え、動作するエラーオブジェクトを作成するために利用可能な場所を使用することです(Benの回答のおかげです)推奨)。そのアプローチに関する私の見解は次のとおりです:https : //gist.github.com/mbrowne/fe45db61cea7858d11be933a998926a8。しかし、いつの日かES6だけを使用できるようになるとすれば、個人的にはそのアプローチの複雑さがそれに値するかどうかはわかりません。


1

これを正しく行う方法は、コンストラクターから適用の結果を返すことと、通常の複雑なJavaScriptの方法でプロトタイプを設定することです。

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

この時点でそれを行うこの方法の唯一の問題は(私は少し繰り返しました)それは

  • stackおよびmessageに含まれないプロパティ以外MyError
  • スタックトレースには、実際には必要ない追加の行があります。

最初の問題は、この回答のトリックを使用して、エラーの列挙できないすべてのプロパティを反復処理することで修正できます。オブジェクトの列挙できない継承されたプロパティ名を取得することは可能ですか?、ただしこれはie <9ではサポートされていません。2番目の問題は、スタックトレースのその行を削除することで解決できますが、安全に実行する方法がわかりません(e.stack.toString()の2行目を削除するだけかもしれません)。


エラーを含む、ほとんどの通常の古いJavaScriptオブジェクトを拡張できるモジュールを作成しました。この時点でかなり成熟していますgithub.com/fresheneesz/proto
BT

1

スニペットはそれをすべて示しています。

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

0

私は一歩下がって、なぜあなたがそれをしたいのかを考えますか?異なるエラーを異なる方法で処理することがポイントだと思います。

たとえば、Pythonでは、catchステートメントをcatchのみに制限MyValidationErrorできます。また、おそらくJavaScriptで同様のことができるようにしたいとします。

catch (MyValidationError e) {
    ....
}

JavaScriptではこれを行うことはできません。キャッチブロックは1つだけです。エラーに対してifステートメントを使用して、そのタイプを判別することになっています。

catch(e) { if(isMyValidationError(e)) { ... } else { // maybe rethrow? throw e; } }

代わりに、タイプ、メッセージ、その他の適切なプロパティを含む生のオブジェクトをスローすると思います。

throw { type: "validation", message: "Invalid timestamp" }

そしてエラーをキャッチすると:

catch(e) {
    if(e.type === "validation") {
         // handle error
    }
    // re-throw, or whatever else
}

1
オブジェクトを投げることは良い考えではありません。がありませんerror.stack。標準のツールでは機能しませんなど。エラーインスタンスにプロパティを追加するのがより良い方法です。例var e = new Error(); e.type = "validation"; ...
timruffles

0

カスタムエラーデコレータ

これはGeorge Baileyの回答に基づいていますが、元のアイデアを拡張して単純化しています。CoffeeScriptで記述されていますが、JavaScriptへの変換は簡単です。アイデアは、ベイリーのカスタムエラーをラップするデコレータで拡張し、新しいカスタムエラーを簡単に作成できるようにすることです。

注:これはV8でのみ機能します。Error.captureStackTrace他の環境ではサポートされていません。

定義する

デコレータはエラータイプの名前を取り、エラーメッセージを受け取り、エラー名を囲む関数を返します。

CoreError = (@message) ->

    @constructor.prototype.__proto__ = Error.prototype
    Error.captureStackTrace @, @constructor
    @name = @constructor.name

BaseError = (type) ->

    (message) -> new CoreError "#{ type }Error: #{ message }"

使用する

これで、新しいエラータイプを簡単に作成できます。

StorageError   = BaseError "Storage"
SignatureError = BaseError "Signature"

おもしろいのは、SignatureError引数が多すぎると呼び出された場合にaをスローする関数を定義することです。

f = -> throw SignatureError "too many args" if arguments.length

これはかなりよくテストされており、V8で完全に動作し、トレースバック、位置などを維持しているようです。

注:newカスタムエラーを作成する場合、使用はオプションです。


0

エラーのパフォーマンスを気にしない場合、これはあなたができる最小のことです

Object.setPrototypeOf(MyError.prototype, Error.prototype)
function MyError(message) {
    const error = new Error(message)
    Object.setPrototypeOf(error, MyError.prototype);
    return error
}

新しいMyError(message)なしで使用できます

コンストラクタErrorが呼び出された後にプロトタイプを変更することにより、コールスタックとメッセージを設定する必要がなくなります


0

Mohsenは上記のES6で名前を設定する優れた回答を持っていますが、TypeScriptを使用している場合、またはこのパブリッククラスとプライベートクラスのフィールドの提案がステージ3を超えて提案として作成された将来に住んでいる場合ECMAScript / JavaScriptの一部としてステージ4に入ると、これが少し短いことを知りたい場合があります。ステージ3では、ブラウザーが機能の実装を開始するため、ブラウザーがそれをサポートしている場合、以下のコードが機能する可能性があります。(新しいEdgeブラウザーv81でテストされ、正常に動作しているようです)。これは現時点では不安定な機能であり、注意して使用する必要があります。また、不安定な機能のブラウザサポートを常に確認する必要があります。この投稿は主に、ブラウザーがこれをサポートする可能性がある将来の居住者向けです。サポートチェックをチェックする MDNにはすることができます私の使用。これは、現在、あなたが本当にそれを今使用したいなどtranspilerどちらか待つ使用しない場合は、そこではなく、その偉大そうになっているブラウザ市場全体の66%をサポート持ってバベルなど何か活字体を

class EOFError extends Error { 
  name="EOFError"
}
throw new EOFError("Oops errored");

これを、スローされたときに名前を記録しない名前のないエラーと比較してください。

class NamelessEOFError extends Error {}
throw new NamelessEOFError("Oops errored");

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