回答:
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
キーワードセットsourceURL
とline
プロパティがあります。それらは正しいことが保証されています。
私が使用したテストケースはここにあります:JavaScript自作エラーオブジェクト比較。
function MyError(message) { this.message = message; this.stack = Error().stack; } MyError.prototype = Object.create(Error.prototype); MyError.prototype.name = "MyError";
MyError.prototype.constructor = MyError
も追加します。
this
か?
ES6の場合:
class MyError extends Error {
constructor(message) {
super(message);
this.name = 'MyError';
}
}
var supportsClasses = false; try {eval('class X{}'); supportsClasses = true;} catch (e) {}
this.name = this.constructor.name;
代わりに使用してください。
要するに:
トランスパイラーなしで ES6 を使用している場合:
class CustomError extends Error { /* ... */}
Babelトランスパイラーを使用している場合:
オプション1:babel-plugin-transform-builtin-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クラス拡張することはできませんようにDate
、Array
、DOM
または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
class CustomError extends Error { /* ... */}
はベンダー固有の引数(lineNumber
など)を正しく処理しません。「ES6構文を使用したJavascriptの拡張エラー」はBabel固有であり、ES5ソリューションが使用しconst
、カスタム引数を処理しません。
console.log(new CustomError('test') instanceof CustomError);// false
は執筆時点では真実でしたが、現在は解決されていることに注目してください。実際にはその答えにリンクされ問題が解決されており、我々は正しい動作をテストすることができ、こことのコード貼り付けて、REPLをし、それが正しく、正しいプロトタイプチェーンをインスタンス化するtranspiled取得する方法を見て。
編集:コメントを読んでください。これは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.constructor
てUserError
、それはそれは一度だけ呼び出されるように、あなたは、関数の外にコードを移動することができます。
そのルートに行く場合は、最初に投げる前に必ずそのラインに電話してください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
Error.call(this)
変更するのではなくエラーを返すため、実際には何もしていませんthis
。
UserError.prototype = Error.prototype
誤解を招くです。これは継承を行わないため、同じクラスになります。
Object.setPrototypeOf(this.constructor.prototype, Error.prototype)
は、がに優先されると思いthis.constructor.prototype.__proto__ = Error.prototype
ます。
エラーの種類ごとに定型を回避するために、いくつかの解決策の知恵を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;
か?
name
はすでにプロトタイプに設定されているので、もう必要ありません。削除しました。ありがとう!
では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 }
});
}
setPrototypeOf()
ますか?少なくともMDNによれば.prototype
、コンストラクターでプロパティを設定するだけで同じことを達成できる場合は、それを使用しないことをお勧めします(else
を持たない参照用のブロックで行っているようにsetPrototypeOf
)。
setPrototypeOf
。ただし、それでも必要な場合は(OPからの質問)、組み込みの方法論を使用する必要があります。MDNが示すように、これはオブジェクトのプロトタイプを設定する適切な方法と考えられています。つまり、MDNはプロトタイプを変更しないでください(パフォーマンスと最適化に影響するため)が、必要な場合はを使用しますsetPrototypeOf
。
CustomError.prototype = Object.create(Error.prototype)
)を使用するだけです。また、のObject.setPrototypeOf(CustomError, Error.prototype)
新しいインスタンスのプロトタイプを指定するのではなく、コンストラクタ自体のプロトタイプを設定していますCustomError
。とにかく、2016年に私は、私はまだバベルと一緒にそれを使用する方法を考え出すていますが、エラーを拡張するためのより良い方法は、実際にあると思う:github.com/loganfsmyth/babel-plugin-transform-builtin-extend/...
CustomError.prototype = Object.create(Error.prototype)
プロトタイプも変更しています。ES5には組み込みの拡張/継承ロジックがないため、これを変更する必要があります。あなたが言及しているbabelプラグインも同様のことをしていると思います。
Object.setPrototypeOf
少なくともここでは使用する意味がない理由を説明する要点を作成しました:gist.github.com/mbrowne/4af54767dcb3d529648f5a8aa11d6348。おそらくあなたは書くObject.setPrototypeOf(CustomError.prototype, Error.prototype)
つもりでした-それはもう少し理にかなっています(ただし、を設定するだけの利点はありませんCustomError.prototype
)。
完全を期すために-以前の回答でこのメソッドについて言及していなかったという理由だけで-Node.jsを使用していて、ブラウザーの互換性を気にする必要がない場合は、組み込みで目的の効果を簡単に達成できますモジュール(ここでは公式ドキュメント)。inherits
util
たとえば、最初の引数としてエラーコードを受け取り、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
を作成するメソッドの使用が原因で発生します。動作の詳細については、こちらのドキュメントをご覧ください。stack
CustomError
this.message = this.message;
これは間違っているのですか、それともJSについて知らないクレイジーなことがまだありますか?
クレセントフレッシュの回答の投票数の多い回答は誤解を招くものです。彼の警告は無効ですが、彼が対処していない他の制限があります。
第一に、クレセントの「警告」の段落の論法は意味をなさない。説明は、「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までドリルダウンして、実際のインスタンス化の場所を見つける必要があります。
Crescent Fresh's
削除されたので混乱を招きます。
このソリューションはどうですか?
以下を使用してカスタムエラーをスローする代わりに:
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();
};
new MyErr (arg1, arg2, new Error())
に使用Object.assign
するMyErrコンストラクターthis
一部の人々が言ったように、それは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;
}
}
私の解決策は、提供されている他の回答よりも単純であり、欠点はありません。
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;
};
throw CustomError('err')
代わりに次を実行する必要がありますthrow new CustomError('err')
上記の例ではError.apply
(Error.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';
他の人が言ったようにノードでは、それは簡単です:
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);
}
私は他の人がすでに述べたことに追加したいだけです:
カスタムエラークラスがスタックトレースに正しく表示されるようにするには、カスタムエラークラスのプロトタイプの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)
私の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__=
いない場合は、このソリューションを使用しないでください。
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);
m = new ...
、その後Promise.reject(m)
。必要はありませんが、コードは読みやすくなっています。
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だけを使用できるようになるとすれば、個人的にはそのアプローチの複雑さがそれに値するかどうかはわかりません。
これを正しく行う方法は、コンストラクターから適用の結果を返すことと、通常の複雑な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行目を削除するだけかもしれません)。
スニペットはそれをすべて示しています。
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)
}
}
私は一歩下がって、なぜあなたがそれをしたいのかを考えますか?異なるエラーを異なる方法で処理することがポイントだと思います。
たとえば、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
}
error.stack
。標準のツールでは機能しませんなど。エラーインスタンスにプロパティを追加するのがより良い方法です。例var e = new Error(); e.type = "validation"; ...
これは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
カスタムエラーを作成する場合、使用はオプションです。
エラーのパフォーマンスを気にしない場合、これはあなたができる最小のことです
Object.setPrototypeOf(MyError.prototype, Error.prototype)
function MyError(message) {
const error = new Error(message)
Object.setPrototypeOf(error, MyError.prototype);
return error
}
新しいMyError(message)なしで使用できます
コンストラクタErrorが呼び出された後にプロトタイプを変更することにより、コールスタックとメッセージを設定する必要がなくなります
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");
this.name = 'MyError'
関数の外側を移動して、に変更できMyError.prototype.name = 'MyError'
ます。