2015-05-22 31 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