使用JSON.stringify字符串化一个Error对象是不可能的吗?
使用JSON.stringify字符串化一个Error对象是不可能的吗?
重现问题
我在尝试使用WebSockets传递错误消息时遇到了一个问题。我可以使用JSON.stringify
来满足更广泛的受众来复制我面临的问题:
// node v0.10.15 > var error = new Error('simple error message'); undefined > error [Error: simple error message] > Object.getOwnPropertyNames(error); [ 'stack', 'arguments', 'type', 'message' ] > JSON.stringify(error); '{}'
问题是我最终得到了一个空对象。
我尝试过的
浏览器
我首先尝试离开node.js并在各种浏览器中运行它。Chrome 28版本给出了相同的结果,有趣的是,至少Firefox尝试过但忽略了这条消息:
>>> JSON.stringify(error); // Firebug, Firefox 23 {"fileName":"debug eval code","lineNumber":1,"stack":"@debug eval code:1\n"}
替换函数
然后我查看了Error.prototype。 它显示原型包含诸如toString和toSource等方法。 知道函数无法被stringified,当我调用JSON.stringify时,包含一个替换函数以删除所有函数,但后来意识到它也有一些奇怪的行为:
var error = new Error('simple error message'); JSON.stringify(error, function(key, value) { console.log(key === ''); // true (?) console.log(value === error); // true (?) });
它似乎不能像通常一样遍历对象,因此我无法检查键是否为函数并忽略它。
问题
是否有办法使用JSON.stringify
将原生错误消息字符串化?如果没有,为什么会发生这种行为?
绕过方法
- 坚持使用基于简单字符串的错误消息,或创建个人错误对象并不要依赖原生错误对象。
- 提取属性:
JSON.stringify({ message: error.message, stack: error.stack })
更新
@Ray Toal建议我查看属性描述符,现在明白为什么它不起作用:
var error = new Error('simple error message'); var propertyNames = Object.getOwnPropertyNames(error); var descriptor; for (var property, i = 0, len = propertyNames.length; i < len; ++i) { property = propertyNames[i]; descriptor = Object.getOwnPropertyDescriptor(error, property); console.log(property, descriptor); }
输出:
stack { get: [Function], set: [Function], enumerable: false, configurable: true } arguments { value: undefined, writable: true, enumerable: false, configurable: true } type { value: undefined, writable: true, enumerable: false, configurable: true } message { value: 'simple error message', writable: true, enumerable: false, configurable: true }
键:enumerable: false
。
接受的答案为此问题提供了解决方法。
您可以定义一个Error.prototype.toJSON
来检索代表Error
的普通Object
:
if (!('toJSON' in Error.prototype)) Object.defineProperty(Error.prototype, 'toJSON', { value: function () { var alt = {}; Object.getOwnPropertyNames(this).forEach(function (key) { alt[key] = this[key]; }, this); return alt; }, configurable: true, writable: true });
var error = new Error('testing'); error.detail = 'foo bar'; console.log(JSON.stringify(error)); // {"message":"testing","detail":"foo bar"}
使用Object.defineProperty()
添加toJSON
,而不将其作为一个enumerable
属性本身。
关于修改Error.prototype
,虽然toJSON()
可能未针对特定的Error
定义,但对于一般的对象,该方法仍然被标准化 (参见步骤3)。因此,发生冲突或冲突的风险很小。
不过,为了完全避免这种情况,可以使用JSON.stringify()
的replacer
参数来代替:
function replaceErrors(key, value) { if (value instanceof Error) { var error = {}; Object.getOwnPropertyNames(value).forEach(function (propName) { error[propName] = value[propName]; }); return error; } return value; } var error = new Error('testing'); error.detail = 'foo bar'; console.log(JSON.stringify(error, replaceErrors));
JSON.stringify(err, Object.getOwnPropertyNames(err))
看起来可行
[来自/r/javascript的/u/ub3rgeek的评论] 和 felixfbecker 在下面的评论