为何instanceof在JSON.stringify()中表现异常?decimal.js场景
解决JSON.stringify replacer中Decimal实例检测不一致的问题
你碰到的这个问题其实挺常见的,核心原因在于decimal.js的实例自带了toJSON()方法,而JSON.stringify在序列化过程中会优先调用对象的toJSON()方法——这就导致你的replacer函数拿到的value参数,已经不是原始的Decimal实例了,而是toJSON()返回的转换后的值(默认是字符串格式的数值)。而函数外直接检测myObj.myNum时,你拿到的是未被转换的原始实例,所以两次instanceof的结果才会不一样。
下面给你两种可行的解决方案:
方案1:通过this获取原始实例(推荐)
在replacer函数里(注意要用普通函数,别用箭头函数,不然this会绑定到外层作用域),this指向当前正在被序列化的对象,你可以直接从this里拿到未被转换的原始属性值,再做类型检测:
const Decimal = require('decimal.js'); const myObj = { myNum: new Decimal(0.3) }; console.log('Property "myNum" is a Decimal:', myObj.myNum instanceof Decimal); // 输出 true const replacer = function(key, value) { // 从当前对象拿到原始值,而不是用JSON转换后的value const originalValue = this[key]; if (originalValue instanceof Decimal) { // 这里自定义你的序列化逻辑,比如转成字符串或者结构化对象 return originalValue.toString(); // 或者返回带类型标识的对象,方便后续反序列化: // return { __type: 'Decimal', value: originalValue.toString() }; } return value; }; const jsonStr = JSON.stringify(myObj, replacer); console.log(jsonStr); // 输出 {"myNum":"0.3"}
方案2:禁用Decimal的toJSON方法
如果你不想让JSON.stringify自动转换Decimal实例,可以直接覆盖掉它的toJSON方法,这样replacer里拿到的value就是原始的Decimal实例了:
const Decimal = require('decimal.js'); // 禁用toJSON方法,让JSON.stringify保留原始实例 Decimal.prototype.toJSON = undefined; const myObj = { myNum: new Decimal(0.3) }; console.log('Property "myNum" is a Decimal:', myObj.myNum instanceof Decimal); // 输出 true const replacer = (key, value) => { if (value instanceof Decimal) { return value.toString(); } return value; }; const jsonStr = JSON.stringify(myObj, replacer); console.log(jsonStr); // 输出 {"myNum":"0.3"}
补充说明
ECMAScript的规范里明确规定:JSON.stringify在处理每个值时,会先检查该值是否有toJSON()方法,如果有,就调用这个方法并使用返回的结果来进行后续的序列化流程。decimal.js为了方便默认实现了这个方法,这就是导致你检测结果不一致的根本原因。
内容的提问来源于stack exchange,提问作者Nathan Friend
相关产品推荐
相关产品推荐

