尝试使用Web套接字传递错误消息时遇到问题。我可以复制自己遇到的问题JSON.stringify以迎合更广泛的受众:
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之类的方法。明知功能不能被字符串化,我包括一个替代品函数调用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。
enumerable: false
接受的答案提供了解决此问题的方法。
您可以定义Error.prototype.toJSON来检索Object表示的纯文本Error:
Error.prototype.toJSON
Object
Error
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本身作为属性。
Object.defineProperty()
toJSON
enumerable
关于修改Error.prototype,虽然toJSON()可能没有Error专门针对它进行修改,但是该方法通常仍针对对象进行了标准化(参见:步骤3)。因此,冲突或冲突的风险很小。
Error.prototype
toJSON()
不过,为了完全避免它,可以使用JSON.stringify()的replacer参数代替:
JSON.stringify()
replacer
function replaceErrors(key, value) { if (value instanceof Error) { var error = {}; Object.getOwnPropertyNames(value).forEach(function (key) { error[key] = value[key]; }); return error; } return value; } var error = new Error('testing'); error.detail = 'foo bar'; console.log(JSON.stringify(error, replaceErrors));