throwtry 文Error およびそのサブクラスErrorError の組み込みサブクラスError のサブクラス化error.cause によるエラーのチェイン [ES2022].cause の代替: カスタムエラークラスこの章では、JavaScript が例外をどのように処理するかを説明します。
なぜ JavaScript はもっと頻繁に例外をスローしないのですか?
JavaScript は ES3 まで例外をサポートしていませんでした。そのため、言語とその標準ライブラリでは控えめに使用されています。
次のコードを考えてみましょう。ファイルに保存されたプロファイルを、クラス Profile のインスタンスを持つ配列に読み込みます。
function readProfiles(filePaths) {
const profiles = [];
for (const filePath of filePaths) {
try {
const profile = readOneProfile(filePath);
profiles.push(profile);
} catch (err) { // (A)
console.log('Error in: '+filePath, err);
}
}
}
function readOneProfile(filePath) {
const profile = new Profile();
const file = openFile(filePath);
// ··· (Read the data in `file` into `profile`)
return profile;
}
function openFile(filePath) {
if (!fs.existsSync(filePath)) {
throw new Error('Could not find file '+filePath); // (B)
}
// ··· (Open the file whose path is `filePath`)
}B 行で何が起こるかを見てみましょう。エラーが発生しましたが、問題を処理するのに最適な場所は現在の場所ではなく、A 行です。そこで、現在のファイルをスキップして次のファイルに進むことができます。
したがって
throw 文を使用して問題が発生したことを示します。try-catch 文を使用して問題を処理します。スローすると、次の構文がアクティブになります。
readProfiles(···)
for (const filePath of filePaths)
try
readOneProfile(···)
openFile(···)
if (!fs.existsSync(filePath))
throw
throw は、try 文に遭遇するまで、ネストされた構文を 1 つずつ終了します。実行は、その try 文の catch 句で続行されます。
throwこれは throw 文の構文です。
throw «value»;JavaScript では任意の値をスローできます。ただし、スタックトレースやエラーチェインなどの追加機能をサポートしているため、Error またはそのサブクラスのインスタンスを使用するのが最適です(§24.4 “Error およびそのサブクラス”を参照)。
これにより、次のオプションが残ります。
クラス Error を直接使用します。JavaScript では、インスタンスに独自のプロパティを追加できるため、より静的な言語よりも制約が少なくなります。
const err = new Error('Could not find the file');
err.filePath = filePath;
throw err;Error のサブクラス のいずれかを使用します。
Error をサブクラス化します(詳細は 後で説明します)。
class MyError extends Error {
}
function func() {
throw new MyError('Problem!');
}
assert.throws(
() => func(),
MyError);try 文try 文の最大バージョンは次のようになります。
try {
«try_statements»
} catch (error) {
«catch_statements»
} finally {
«finally_statements»
}これらの句は、次のように組み合わせることができます。
try-catchtry-finallytry-catch-finallytry ブロックtry ブロックは、文の本体と見なすことができます。ここで、通常のコードを実行します。
catch 句例外が try ブロックに到達した場合、その例外は catch 句のパラメータに割り当てられ、その句内のコードが実行されます。次に、実行は通常、try 文の後で続行されます。次の場合は、変更される可能性があります。
catch ブロック内に return、break、または throw がある場合。finally 句がある場合(try 文が終了する前に常に実行されます)。次のコードは、A 行でスローされた値が実際に B 行でキャッチされることを示しています。
const errorObject = new Error();
function func() {
throw errorObject; // (A)
}
try {
func();
} catch (err) { // (B)
assert.equal(err, errorObject);
}catch バインディングの省略 [ES2019]スローされた値に関心がない場合は、catch パラメータを省略できます。
try {
// ···
} catch {
// ···
}これは時々役立つことがあります。たとえば、Node.js には、func 内でエラーがスローされるかどうかを確認する API 関数 assert.throws(func) があります。これは次のように実装できます。
function throws(func) {
try {
func();
} catch {
return; // everything OK
}
throw new Error('Function didn’t throw an exception!');
}ただし、この関数のより完全な実装には、catch パラメータがあり、たとえば、その型が期待どおりであるかどうかを確認します。
finally 句finally 句内のコードは、try ブロックまたは catch 句で何が起こっても、try 文の最後に常に実行されます。
finally の一般的なユースケースを見てみましょう。リソースを作成し、そのリソースの使用が完了したら、そのリソースを常に破棄したいとします。その使用中に何が起こっても構いません。これは次のように実装します。
const resource = createResource();
try {
// Work with `resource`. Errors may be thrown.
} finally {
resource.destroy();
}finally は常に実行されるfinally 句は、エラーがスローされた場合でも(A 行)常に実行されます。
let finallyWasExecuted = false;
assert.throws(
() => {
try {
throw new Error(); // (A)
} finally {
finallyWasExecuted = true;
}
},
Error
);
assert.equal(finallyWasExecuted, true);そして、return 文がある場合でも(A 行)
let finallyWasExecuted = false;
function func() {
try {
return; // (A)
} finally {
finallyWasExecuted = true;
}
}
func();
assert.equal(finallyWasExecuted, true);Error およびそのサブクラスError は、すべての組み込みエラークラスの共通のスーパー クラスです。
Errorこれは、Error のインスタンスプロパティとコンストラクターがどのように見えるかです。
class Error {
// Instance properties
message: string;
cause?: any; // ES2022
stack: string; // non-standard but widely supported
constructor(
message: string = '',
options?: ErrorOptions // ES2022
);
}
interface ErrorOptions {
cause?: any; // ES2022
}コンストラクターには 2 つのパラメータがあります。
message はエラーメッセージを指定します。options は ECMAScript 2022 で導入されました。これには、現在 1 つのプロパティがサポートされているオブジェクトが含まれています。.cause は、現在のエラーの原因となった例外(もしあれば)を指定します。次のサブセクションでは、インスタンスプロパティ .message、.cause、および .stack について詳しく説明します。
Error.prototype.name各組み込みエラークラス E には、プロパティ E.prototype.name があります。
> Error.prototype.name
'Error'
> RangeError.prototype.name
'RangeError'したがって、組み込みエラーオブジェクトのクラス名を取得する方法は 2 つあります。
> new RangeError().name
'RangeError'
> new RangeError().constructor.name
'RangeError'.message.message にはエラーメッセージのみが含まれます。
const err = new Error('Hello!');
assert.equal(String(err), 'Error: Hello!');
assert.equal(err.message, 'Hello!');メッセージを省略すると、空の文字列がデフォルト値として使用されます(Error.prototype.message から継承されます)。
メッセージを省略すると、空の文字列になります。
assert.equal(new Error().message, '');.stackインスタンスプロパティ .stack は ECMAScript の機能ではありませんが、JavaScript エンジンで広くサポートされています。通常は文字列ですが、その正確な構造は標準化されておらず、エンジンによって異なります。
これは JavaScript エンジン V8 での表示例です。
const err = new Error('Hello!');
assert.equal(
err.stack,
`
Error: Hello!
at file://ch_exception-handling.mjs:1:13
`.trim());.cause [ES2022]インスタンスプロパティ .cause は、new Error() の 2 番目のパラメータの options オブジェクトを介して作成されます。これにより、現在のエラーの原因となった他のエラーを指定できます。
const err = new Error('msg', {cause: 'the cause'});
assert.equal(err.cause, 'the cause');このプロパティの使用方法については、§24.5 “エラーのチェイン”を参照してください。
Error の組み込みサブクラスError には、次のサブクラスがあります – ECMAScript 仕様から引用します。
AggregateError [ES2021] は、複数のエラーを一度に表します。標準ライブラリでは、Promise.any() のみが使用します。RangeError は、値が許可される値のセットまたは範囲にないことを示します。ReferenceError は、無効な参照値が検出されたことを示します。SyntaxError は、解析エラーが発生したことを示します。TypeError は、他の NativeError オブジェクトのいずれも失敗の原因を適切に示すものではない場合に、操作が失敗したことを示すために使用されます。URIError は、グローバル URI 処理関数のいずれかが、その定義と互換性のない方法で使用されたことを示します。Error のサブクラス化ECMAScript 2022 以降、Error コンストラクターは 2 つのパラメータを受け入れます(前のサブセクションを参照)。したがって、それをサブクラス化する際には、2 つの選択肢があります。サブクラスでコンストラクターを省略するか、次のように super() を呼び出すかです。
class MyCustomError extends Error {
constructor(message, options) {
super(message, options);
// ···
}
}場合によっては、より深くネストされた関数呼び出し中にスローされたエラーをキャッチし、それに追加情報を添付したい場合があります。
function readFiles(filePaths) {
return filePaths.map(
(filePath) => {
try {
const text = readText(filePath);
const json = JSON.parse(text);
return processJson(json);
} catch (error) {
// (A)
}
});
}try 句内のステートメントは、あらゆる種類のエラーをスローする可能性があります。ほとんどの場合、エラーはエラーの原因となったファイルのパスを認識しません。そのため、A 行でその情報を添付したいのです。
error.cause によるエラーのチェイン [ES2022]ECMAScript 2022 以降、new Error() では、その原因を指定できます。
function readFiles(filePaths) {
return filePaths.map(
(filePath) => {
try {
// ···
} catch (error) {
throw new Error(
`While processing ${filePath}`,
{cause: error}
);
}
});
}.cause の代替: カスタムエラークラス次のカスタムエラークラスは、チェインをサポートしています。.cause と前方互換性があります。
/**
* An error class that supports error chaining.
* If there is built-in support for .cause, it uses it.
* Otherwise, it creates this property itself.
*
* @see https://github.com/tc39/proposal-error-cause
*/
class CausedError extends Error {
constructor(message, options) {
super(message, options);
if (
(isObject(options) && 'cause' in options)
&& !('cause' in this)
) {
// .cause was specified but the superconstructor
// did not create an instance property.
const cause = options.cause;
this.cause = cause;
if ('stack' in cause) {
this.stack = this.stack + '\nCAUSE: ' + cause.stack;
}
}
}
}
function isObject(value) {
return value !== null && typeof value === 'object';
} 演習: 例外処理
exercises/exception-handling/call_function_test.mjs
クイズ
クイズアプリを参照してください。