簡體   English   中英

使用 ES6 語法和 Babel 擴展 Javascript 中的錯誤

[英]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 對象永遠不會得到正確的消息集。

嘗試使用 Babel REPL

現在我已經在 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-b​​uiltin-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);

在 REPL 中嘗試

終於把這件事平息了。 在 Babel 6 中,開發人員明確表示不支持從內置擴展。雖然這個技巧對MapSet沒有幫助,但它確實適用於Error 這很重要,因為可以拋出異常的語言的核心思想之一是允許自定義錯誤。 這一點很重要,因為 Promise 變得越來越有用,因為它們旨在拒絕 Error

可悲的事實是您仍然需要在 ES2015 中以舊方式執行此操作。

Babel REPL 中的示例

自定義錯誤模式

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-b​​uiltin-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-b​​uiltin-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 consoleNode.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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM