2015-05-22 84 views
11

當通過BabelJS運行這段代碼:擴展錯誤沒有消息或堆棧跟蹤

class FooError extends Error { 
    constructor(message) { 
    super(message); 
    } 
} 

let error = new FooError('foo'); 
console.log(error, error.message, error.stack); 

它輸出

{} 

這不是我所期望的。運行

error = new Error('foo'); 
console.log(error, error.message, error.stack); 

產生

{} foo Error: foo 
    at eval (eval at <anonymous> (https://babeljs.io/scripts/repl.js?t=2015-05-21T16:46:33+00:00:263:11), <anonymous>:24:9) 
    at REPL.evaluate (https://babeljs.io/scripts/repl.js?t=2015-05-21T16:46:33+00:00:263:36) 
    at REPL.compile (https://babeljs.io/scripts/repl.js?t=2015-05-21T16:46:33+00:00:210:12) 
    at Array.onSourceChange (https://babeljs.io/scripts/repl.js?t=2015-05-21T16:46:33+00:00:288:12) 
    at u (https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js:28:185) 

這正是我想從擴展的錯誤一樣。

我的目標是將Error擴展成各種子類,並將它們用於藍鳥的catch匹配。到目前爲止,這是失敗的悲慘。

爲什麼子類不顯示消息或堆棧跟蹤?

編輯:using Chrome's built-in subclassing(感謝@coder)完美地工作。這是不特定於通天,不一定,如下面的示例(來自@loganfsmyth on Babel's gitter feed)表示:

// Works 
new (function(){ 
    "use strict"; 
    return class E extends Error { } 
}()); 
// Doesn't 
new (function(){ 
    "use strict"; 
    function E(message){ 
    Error.call(this, message); 
    }; 
    E.prototype = Object.create(Error); 
    E.prototype.constructor = E; 
    return E; 
}()); 
+1

我不認爲這是一個巴別塔問題。如果您使用舊的方式來擴展錯誤,您將得到相同的缺失堆棧(在Chromium41上)。 –

+1

你能確認你使用的瀏覽器嗎? Chrome 42上的chrome v42 https://jsfiddle.net/5e3kakqj/ – coder

+1

@coder也是如此。你的例子有效,但Babel版本沒有。 – ssube

回答

9

總之,用巴貝爾的transpiled代碼僅適用於建在一個特定的方式類延伸和大量的原生東西似乎並不像那樣。 Babel的文檔警告說,擴展許多本地類無法正常工作。

您可以創建一個屬性 「手動」 創建一個緩衝級,這樣的事情:爲什麼不

class FooError extends ErrorClass { 
    constructor(message) { 
    super(message); 
    } 
} 

class ErrorClass extends Error { 
    constructor (message) { 
    super(); 

    if (Error.hasOwnProperty('captureStackTrace')) 
     Error.captureStackTrace(this, this.constructor); 
    else 
     Object.defineProperty(this, 'stack', { 
      value: (new Error()).stack 
     }); 

    Object.defineProperty(this, 'message', { 
     value: message 
    }); 
    } 

} 

然後擴展該類代替它如你所期望的那樣工作?

如果你看看什麼是transpiled,你會看到巴貝爾首先分配超類的原型到子類的副本,那麼當你調用new SubClass()調用此函數:

_get(Object.getPrototypeOf(FooError.prototype), "constructor", this).call(this, message) 

哪裏_GET是注入腳本輔助函數:

(function get(object, property, receiver) { 
    var desc = Object.getOwnPropertyDescriptor(object, property); 

    if (desc === undefined) { 
    var parent = Object.getPrototypeOf(object); 

    if (parent === null) { 
     return undefined; 
    } else { 
     return get(parent, property, receiver); 
    } 
    } else if ("value" in desc) { 
    return desc.value; 
    } else { 
    var getter = desc.get; 

    if (getter === undefined) { 
     return undefined; 
    } 

    return getter.call(receiver); 
    } 
}); 

它有點像找到了子類原型的原型的constructor屬性描述符,並試圖調用它的getter無線如果新的子類實例存在或返回其值(if ("value" in desc)),則在此情況下爲錯誤構造函數本身。它不會爲超級調用this分配任何東西,所以當新對象具有正確的原型時,它不會按照您期望的方式構建。基本上,超級調用對新構建的對象沒有任何作用,只是創建一個新的Error,它不分配給任何東西。

如果我們使用上面定義的ErrorClass,它確實符合巴貝爾預期的類結構。

+0

這似乎是正確的。您只能使用本機語法擴展某些本機對象。 – ssube

+0

我認爲,當ES2015類在瀏覽器中本地運行時(這種情況發生在ES5上,因爲某些功能無法實現勻場),這可以正常工作。 – trusktr