扩展错误没有消息或堆栈跟踪
Extended Errors do not have message or stack trace
当运行这段代码通过 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
扩展成各种子类,并在bluebird的catch
匹配中使用它们。到目前为止,这是惨败。
为什么子类不显示消息或堆栈跟踪?
编辑: using Chrome's built-in subclassing (thanks to @coder) works perfectly. This isn't specific to Babel, necessarily, as the following example (from @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;
}());
简而言之,使用 babel 的转译代码进行扩展仅适用于以特定方式构建的 classes,而且很多原生的东西似乎并不是那样构建的。 Babel 的文档警告说,扩展许多原生 classes 无法正常工作。
您可以创建一个缓冲区 class 来创建属性 "manually",如下所示:
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
});
}
}
然后扩展 class:
class FooError extends ErrorClass {
constructor(message) {
super(message);
}
}
为什么它没有像您预期的那样工作?
如果你看看转译的内容,你会看到 babel 首先将超级 class' 原型的副本分配给子 class,然后当你调用 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);
}
});
它做了一些事情,比如找到子 class 原型的 constructor
属性 描述符,并尝试用新的子 [=40] 调用它的 getter =] 实例作为上下文,如果它存在或 return 它的值 (if ("value" in desc)
),在这种情况下是 Error 构造函数本身。它不会从 super 调用中为 this
分配任何内容,因此虽然新对象具有正确的原型,但它并没有按照您期望的方式构建。基本上 super 调用对新构造的对象没有任何作用,只是创建一个新的 Error
,它没有分配给任何东西。
如果我们使用上面定义的 ErrorClass
,它确实符合 Babel 预期的 class 结构。
这是由于 ES6 到 ES5 的下层编译造成的限制。在 TypeScript 的 explanation.
中找到更多相关信息
作为解决方法,您可以执行以下操作:
class QueryLimitError extends Error {
__proto__: QueryLimitError;
constructor(message) {
const trueProto = new.target.prototype;
super(message);
this.__proto__ = trueProto;
}
}
在某些情况下 Object.setPrototypeOf(this, FooError.prototype);
可能就足够了。
您将在相应的 Github issue 和
中找到更多信息
当运行这段代码通过 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
扩展成各种子类,并在bluebird的catch
匹配中使用它们。到目前为止,这是惨败。
为什么子类不显示消息或堆栈跟踪?
编辑: using Chrome's built-in subclassing (thanks to @coder) works perfectly. This isn't specific to Babel, necessarily, as the following example (from @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;
}());
简而言之,使用 babel 的转译代码进行扩展仅适用于以特定方式构建的 classes,而且很多原生的东西似乎并不是那样构建的。 Babel 的文档警告说,扩展许多原生 classes 无法正常工作。
您可以创建一个缓冲区 class 来创建属性 "manually",如下所示:
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
});
}
}
然后扩展 class:
class FooError extends ErrorClass {
constructor(message) {
super(message);
}
}
为什么它没有像您预期的那样工作?
如果你看看转译的内容,你会看到 babel 首先将超级 class' 原型的副本分配给子 class,然后当你调用 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);
}
});
它做了一些事情,比如找到子 class 原型的 constructor
属性 描述符,并尝试用新的子 [=40] 调用它的 getter =] 实例作为上下文,如果它存在或 return 它的值 (if ("value" in desc)
),在这种情况下是 Error 构造函数本身。它不会从 super 调用中为 this
分配任何内容,因此虽然新对象具有正确的原型,但它并没有按照您期望的方式构建。基本上 super 调用对新构造的对象没有任何作用,只是创建一个新的 Error
,它没有分配给任何东西。
如果我们使用上面定义的 ErrorClass
,它确实符合 Babel 预期的 class 结构。
这是由于 ES6 到 ES5 的下层编译造成的限制。在 TypeScript 的 explanation.
中找到更多相关信息作为解决方法,您可以执行以下操作:
class QueryLimitError extends Error {
__proto__: QueryLimitError;
constructor(message) {
const trueProto = new.target.prototype;
super(message);
this.__proto__ = trueProto;
}
}
在某些情况下 Object.setPrototypeOf(this, FooError.prototype);
可能就足够了。
您将在相应的 Github issue 和
中找到更多信息