標準のErrorを拡張してプロパティを持ったCustomErrorを作成するのに、
はまってしまってやっと解決したので共有します。
#継承のおさらい
まずは、何の変哲もない継承の方法からおさらいします。
node.jsで普通に継承を行うと次のように実装する事が出来ます
var util = require('util');
// 継承元
var Vehicle = function Vehicle (capacity) {
this.capacity = capacity;
};
Vehicle.prototype.capacity = 0;
// 継承先
var Car = function Car (capacity, color) {
Car.super_.call(this, capacity);
if (color) {
this.color = color;
}
};
util.inherits(Car, Vehicle);
Car.prototype.color = 'black';
// 使用
var c = new Car (20, 'red');
console.log(c);
##補足
継承にはutilモジュールを使用しています。
使用しない方法もありますが、nodeでは標準で用意されているutilを使用する方が一般的で分かりやすいので
util.inheritsを採用しました。
継承先のコンストラクタから継承元のコンストラクタを利用する方法は、super_を利用する事で
簡単に出来ます。
super_はutil.inheritsで勝手に追加されていますが、これを使うのはちょっと嫌だと思う場合は
代わりに次のようにしても同じ結果です。
Vehicle.call(this, capacity);
ただし、この場合は継承先コンストラクタの中に継承元コンストラクタを明記しなくてはならないので
毎回間違えないように気をつける必要があります。
なのでsuper_を使用する方が楽です。
#なぜかうまくいかないエラーの継承
さて本題のCustomErrorを作成に取りかかります。
とりあえず次のように記述してみます。
// 継承先
var CustomError = function CustomError (message, errNo) {
CustomError.super_.call(this, message);
this.errNo = errNo;
};
util.inherits(CustomError, Error);
CustomError.prototype.errNo = null;
// 使用
var err = new CustomError ('エラーだよ' ,999);
console.log(err instanceof Error); // true
console.log(err.errNo); // 999
console.log(err.message); // 空文字
console.log(err.name); // 'Error'
console.log(err.stack); // undefined
結果で、思うように動作していない事が確認出来ます
実はErrorオブジェクトはコンストラクタでstackを作成しているわけではありません。
そのため、messageもstackも値が入っていません。
#ベタ書きの実装
仕方がないのでコンストラクタでmessageとstackに値を入れるように実装します
captureStackTraceによってstackに値を設定する事が出来ます。
node(V8)でのみ使用できるメソッドのためほとんど情報がありませんが、
一番目の引数のオブジェクトにstackプロパティを追加します。
二番目の引数は省略しても動作しますが、自身のコンストラクタを追加しておく事で
stackに自身の関数が列挙されなくなるため、指定しておくほうがよいと思われます。
ついでにnameもErrorではなくCustomErrorと入るようにします。
// 継承先
var CustomError = function CustomError (message, errNo) {
this.message = message || 'Error';
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
this.errNo = errNo;
};
util.inherits(CustomError, Error);
CustomError.prototype.errNo = null;
// 使用
var err = new CustomError ('エラーだよ' ,999);
console.log(err instanceof Error); // true
console.log(err.errNo); // 999
console.log(err.message); // 'エラーだよ'
console.log(err.name); // 'CustomError'
console.log(err.stack); // CustomError: エラーだよ
// at Object.<anonymous> (・・・/customrrror2.js:14:11)
// at Module._compile (module.js:456:26)
// ・・・省略
うまくいきました。
独自のプロパティを追加する事もできていますので、いろいろと捗りそうです。
#抽象コンストラクタ
でもやっぱり継承を普段通りに実装したいというか、いちいちcaptureStackTraceを思い出して
書くのもいやだと。。
なので、抽象コンストラクタを作成して普段通りの記述ができるようにします。
var util = require('util');
// 継承元(抽象コンストラクタ)
var AbstractError = function (message) {
this.message = message || 'Error';
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
};
util.inherits(AbstractError, Error);
// 継承先
var CustomError = function CustomError (message, errNo) {
CustomError.super_.call(this, message);
this.errNo = errNo;
};
util.inherits(CustomError, AbstractError);
CustomError.prototype.errNo = null;
// 使用(省略)
抽象エラーコンストラクタのおかげで、通常の継承方法でエラーを継承する事ができました。
#注意
この方法は、将来も保証されているとは限りません。
utilの仕様がかわりsuper_プロパティが廃止されたり、非標準のcaptureStackTraceを使用している為です
とはいえ、server-sideで使用する分にはバージョンが変わらない限り有効だと思います。