[英]Extending Error in Javascript with ES6 syntax & Babel
我正在嘗試使用 ES6 和 Babel 擴展 Error。 它行不通。
class MyError extends Error {
constructor(m) {
super(m);
}
}
var error = new Error("ll");
var myerror = new MyError("ll");
console.log(error.message) //shows up correctly
console.log(myerror.message) //shows empty string
Error 對象永遠不會得到正確的消息集。
現在我已經在 SO 上看到了一些解決方案( 例如這里),但它們看起來都非常不符合 ES6 標准。 如何以一種不錯的 ES6 方式做到這一點? (這是在 Babel 中工作的)
根據 Karel Bílek 的回答,我會對constructor
進行一些小的更改:
class ExtendableError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
if (typeof Error.captureStackTrace === 'function') {
Error.captureStackTrace(this, this.constructor);
} else {
this.stack = (new Error(message)).stack;
}
}
}
// now I can extend
class MyError extends ExtendableError {}
var myerror = new MyError("ll");
console.log(myerror.message);
console.log(myerror instanceof Error);
console.log(myerror.name);
console.log(myerror.stack);
這將在堆棧中打印MyError
,而不是通用Error
。
它還會將錯誤消息添加到堆棧跟蹤中——這是 Karel 的示例中缺少的。
如果可用,它還將使用captureStackTrace
。
使用 Babel 6,你需要transform-builtin-extend ( npm ) 才能工作。
結合這個答案,這個答案和這個代碼,我制作了這個小“助手”類,它似乎工作正常。
class ExtendableError extends Error {
constructor(message) {
super();
this.message = message;
this.stack = (new Error()).stack;
this.name = this.constructor.name;
}
}
// now I can extend
class MyError extends ExtendableError {
constructor(m) {
super(m);
}
}
var myerror = new MyError("ll");
console.log(myerror.message);
console.log(myerror instanceof Error);
console.log(myerror.name);
console.log(myerror.stack);
終於把這件事平息了。 在 Babel 6 中,開發人員明確表示不支持從內置擴展。雖然這個技巧對Map
、 Set
等沒有幫助,但它確實適用於Error
。 這很重要,因為可以拋出異常的語言的核心思想之一是允許自定義錯誤。 這一點很重要,因為 Promise 變得越來越有用,因為它們旨在拒絕 Error 。
可悲的事實是您仍然需要在 ES2015 中以舊方式執行此操作。
class MyError {
constructor(message) {
this.name = 'MyError';
this.message = message;
this.stack = new Error().stack; // Optional
}
}
MyError.prototype = Object.create(Error.prototype);
另一方面,Babel 6 有一個插件可以實現這一點。
https://www.npmjs.com/package/babel-plugin-transform-builtin-extend
更新:(截至 2016-09-29)經過一些測試,babel.io 似乎沒有正確解釋所有斷言(從自定義擴展錯誤擴展)。 但在 Ember.JS 中,擴展錯誤按預期工作: https ://ember-twiddle.com/d88555a6f408174df0a4c8e0fd6b27ce
編輯: Typescript 2.1中的重大更改
擴展 Error、Array 和 Map 等內置函數可能不再有效。
作為建議,您可以在任何 super(...) 調用后立即手動調整原型。
稍微編輯 Lee Benson 的原始答案對我有用。 這還將向實例添加ExtendableError
類的stack
和其他方法。
class ExtendableError extends Error {
constructor(message) {
super(message);
Object.setPrototypeOf(this, ExtendableError.prototype);
this.name = this.constructor.name;
}
dump() {
return { message: this.message, stack: this.stack }
}
}
class MyError extends ExtendableError {
constructor(message) {
super(message);
Object.setPrototypeOf(this, MyError.prototype);
}
}
var myerror = new MyError("ll");
console.log(myerror.message);
console.log(myerror.dump());
console.log(myerror instanceof Error);
console.log(myerror.name);
console.log(myerror.stack);
隨着 babel 6 的最新變化,我發現transform-builtin-extend不再有效。 我最終使用了這種混合方法:
export default class MyError {
constructor (message) {
this.name = this.constructor.name;
this.message = message;
this.stack = (new Error(message)).stack;
}
}
MyError.prototype = Object.create(Error.prototype);
MyError.prototype.constructor = MyError;
和
import MyError from './MyError';
export default class MyChildError extends MyError {
constructor (message) {
super(message);
}
}
結果,所有這些測試都通過了:
const sut = new MyError('error message');
expect(sut.message).toBe('error message');
expect(sut).toBeInstanceOf(Error);
expect(sut).toBeInstanceOf(MyError);
expect(sut.name).toBe('MyError');
expect(typeof sut.stack).toBe('string');
const sut = new MyChildError('error message');
expect(sut.message).toBe('error message');
expect(sut).toBeInstanceOf(Error);
expect(sut).toBeInstanceOf(MyError);
expect(sut).toBeInstanceOf(MyChildError);
expect(sut.name).toBe('MyChildError');
expect(typeof sut.stack).toBe('string');
class MyError extends Error {
constructor(message) {
super(message);
this.message = message;
this.name = 'MyError';
}
}
不需要
this.stack = (new Error()).stack;
技巧感謝super()
調用。
雖然上面的代碼不能輸出堆棧跟蹤,除非this.stack = (new Error()).stack;
或Error.captureStackTrace(this, this.constructor.name);
在Babel 中調用。 IMO,這可能是這里的一個問題。
實際上,可以使用此代碼片段在Chrome console
和Node.js v4.2.1
下輸出堆棧跟蹤。
class MyError extends Error{
constructor(msg) {
super(msg);
this.message = msg;
this.name = 'MyError';
}
};
var myerr = new MyError("test");
console.log(myerr.stack);
console.log(myerr);
Chrome console
輸出。
MyError: test
at MyError (<anonymous>:3:28)
at <anonymous>:12:19
at Object.InjectedScript._evaluateOn (<anonymous>:875:140)
at Object.InjectedScript._evaluateAndWrap (<anonymous>:808:34)
at Object.InjectedScript.evaluate (<anonymous>:664:21)
Node.js
輸出
MyError: test
at MyError (/home/bsadmin/test/test.js:5:8)
at Object.<anonymous> (/home/bsadmin/test/test.js:11:13)
at Module._compile (module.js:435:26)
at Object.Module._extensions..js (module.js:442:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:311:12)
at Function.Module.runMain (module.js:467:10)
at startup (node.js:134:18)
at node.js:961:3
除了@zangw 答案,您還可以這樣定義錯誤:
'use strict';
class UserError extends Error {
constructor(msg) {
super(msg);
this.name = this.constructor.name;
}
}
// define errors
class MyError extends UserError {}
class MyOtherError extends UserError {}
console.log(new MyError instanceof Error); // true
throw new MyError('My message');
這將拋出正確的名稱、消息和堆棧跟蹤:
MyError: My message
at UserError (/Users/honzicek/Projects/api/temp.js:5:10)
at MyError (/Users/honzicek/Projects/api/temp.js:10:1)
at Object.<anonymous> (/Users/honzicek/Projects/api/temp.js:14:7)
at Module._compile (module.js:434:26)
at Object.Module._extensions..js (module.js:452:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Function.Module.runMain (module.js:475:10)
at startup (node.js:117:18)
at node.js:951:3
我正在嘗試使用 ES6 擴展 Error
那個class MyError extends Error {…}
語法是正確的。
請注意,轉譯器在繼承內置對象方面仍然存在問題。 在你的情況下,
var err = super(m);
Object.assign(this, err);
似乎解決了這個問題。
鑒於此,接受的答案不再有效,您可以始終使用工廠作為替代方案( repl ):
function ErrorFactory(name) { return class AppError extends Error { constructor(message) { super(message); this.name = name; this.message = message; if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, this.constructor); } else { this.stack = (new Error(message)).stack; } } } } // now I can extend const MyError = ErrorFactory("MyError"); var myerror = new MyError("ll"); console.log(myerror.message); console.log(myerror instanceof Error); console.log(myerror.name); console.log(myerror.stack);
我更喜歡比上面描述的更強大的語法。 錯誤類型的其他方法將幫助您創建漂亮的console.log
或其他內容。
export class CustomError extends Error {
/**
* @param {string} message
* @param {number} [code = 0]
*/
constructor(message, code = 0) {
super();
/**
* @type {string}
* @readonly
*/
this.message = message;
/**
* @type {number}
* @readonly
*/
this.code = code;
/**
* @type {string}
* @readonly
*/
this.name = this.constructor.name;
/**
* @type {string}
* @readonly
*/
this.stack = CustomError.createStack(this);
}
/**
* @return {string}
*/
toString() {
return this.getPrettyMessage();
}
/**
* @return {string}
*/
getPrettyMessage() {
return `${this.message} Code: ${this.code}.`;
}
/**
* @param {CustomError} error
* @return {string}
* @private
*/
static createStack(error) {
return typeof Error.captureStackTrace === 'function'
? Error.captureStackTrace(error, error.constructor)
: (new Error()).stack;
}
}
要測試此代碼,您可以運行類似的東西:
try {
throw new CustomError('Custom error was thrown!');
} catch (e) {
const message = e.getPrettyMessage();
console.warn(message);
}
歡迎擴展CustomError
類型。 可以向擴展類型添加一些特定功能或覆蓋現有功能。 例如。
export class RequestError extends CustomError {
/**
* @param {string} message
* @param {string} requestUrl
* @param {number} [code = 0]
*/
constructor(message, requestUrl, code = 0) {
super(message, code);
/**
* @type {string}
* @readonly
*/
this.requestUrl = requestUrl;
}
/**
* @return {string}
*/
getPrettyMessage() {
const base = super.getPrettyMessage();
return `${base} Request URL: ${this.requestUrl}.`;
}
}
正如@sukima 提到的,你不能擴展原生 JS。 OP的問題無法回答。
與Melbourne2991 的回答類似,我確實使用了工廠,但遵循MDN 對客戶錯誤類型的建議。
function extendError(className){
function CustomError(message){
this.name = className;
this.message = message;
this.stack = new Error().stack; // Optional
}
CustomError.prototype = Object.create(Error.prototype);
CustomError.prototype.constructor = CustomError;
return CustomError;
}
這對我有用:
/**
* @class AuthorizationError
* @extends {Error}
*/
export class AuthorizationError extends Error {
message = 'UNAUTHORIZED';
name = 'AuthorizationError';
}
不使用 Babel,但在普通的 ES6 中,以下對我來說似乎工作正常:
class CustomError extends Error {
constructor(...args) {
super(...args);
this.name = this.constructor.name;
}
}
來自 REPL 的測試:
> const ce = new CustomError('foobar');
> ce.name
'CustomError'
> ce.message
'foobar'
> ce instanceof CustomError
true
> ce.stack
'CustomError: foobar\n at CustomError (repl:3:1)\n ...'
如您所見,堆棧包含錯誤名稱和消息。 我不確定我是否遺漏了什么,但所有其他答案似乎都使事情過於復雜。
我通過這種方式改進了@Lee Benson 的解決方案:
可擴展錯誤.js
class ExtendableError extends Error {
constructor(message, errorCode) {
super(message);
this.name = this.constructor.name;
this.errorCode = errorCode
if (typeof Error.captureStackTrace === 'function') {
Error.captureStackTrace(this, this.constructor);
} else {
this.stack = (new Error(message)).stack;
}
}
}
export default ExtendableError
錯誤示例
import ExtendableError from './ExtendableError'
const AuthorizationErrors = {
NOT_AUTHORIZED: 401,
BAD_PROFILE_TYPE: 402,
ROLE_NOT_ATTRIBUTED: 403
}
class AuthorizationError extends ExtendableError {
static errors = AuthorizationErrors
}
export default AuthorizationError
然后,您可以在使用選項說明符的同時對錯誤進行分組,以決定在某些特定於應用程序的情況下采取不同的措施
new AuthorizationError ("The user must be a seller to be able to do a discount", AuthorizationError.errors.BAD_PROFILE_TYPE )
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.