就我目前4年( 实习了1年,965了1年,996了2年,算3年感觉少了,说是4年老司机也不为过吧。)的工作经验来看,JSON.stringify一般有以下用途:
- 深拷贝:深拷贝引用类型的数据
- 序列化:服务端存储重依赖前端的数据,localStorage/sessionStorage存储的数据
但其实除了上面两种常用的用法之外,JSON.stringify还有许多更加强大的特性,值得系统性学习。 关键是这些特性可能是你开发过程中经常用到的,只是你不知道而已。
也许你觉得这是一篇枯燥的长篇大论的讲用法的博文,那么我就说几个有趣的场景吧:
- 为什么这个对象明明有foo属性,序列化后在数据库持久化存储之后,这个属性咋就没了呢?
- 为什么axios发送post请求时,如果req的body包含undefined值的参数,在发给服务端的请求中会消失?
- 打印出的JSON字符串就这么不易读么?除了store成一个variable然后parse之外,能不能不parse就清晰看到它的数据结构呢?
- 为什么序列化一个对象,它还报错呢?报的还是那种自己引用自己的错误?
如果这几个问题看了之后是一脸懵逼,那么一定要细细阅读一下这篇博文,答案就在文中。聪明的你一定会找到答案的。
- 工作中常用JSON.stringify()
- 深拷贝:深拷贝引用类型的数据(JSON.parse(JSON.stringify(obj/arr)))
- 序列化:服务端存储重依赖前端的数据, localStorage/sessionStorage存储的数据(例如fabric.js的canvas模板数据,vue-amap的svg路径信息等等
- 存储重前端功能的数据
- localStorage/sessionStorage存储的数据
- 查询JSON数据做解析
- 初识JSON.stringify()
- 序列化数据
- 过滤数据
- 格式化数据
- JSON.stringify(value, [ ,replacer[ ,space]]) 语法
- 参数
- value
- replacer(可选)
- space(可选)
- 返回值
- 异常
- TypeError “cyclic object value”
- TypeError “BigInt value can't be serialized in JSON”
- JSON.stringify()描述
- 常见使用须知
- replacer 参数
- 参数为函数
- 参数为数组
- space 参数
- 参数为number
- 参数为string
- toJSON()的表现
- JSON.stringify()序列化循环引用时的问题
- 自引用对象抛出TypeError
- JSON.stringify高德地图vue-amap中的自引用对象
- 如何序列化自引用对象?
- JSON.stringify()序列化具有相同属性相同值但属性顺序不同的对象,结果相等吗?
- JOSN.stringify()与localStorage的使用示例
- 回答一下文章开头的问题
工作中常用JSON.stringify()
深拷贝:深拷贝引用类型的数据(JSON.parse(JSON.stringify(obj/arr)))
深拷贝之后,deepCopy会生成一个内存独立的obj或者arr。 也就是说obj/arr与deepCopy存储在不同的堆内存,修改obj/arr不会影响deepCopy,修改deepCopy也不会影响obj或者arr。
若是对于深浅拷贝不理解,建议先找资料系统性学习一下。
序列化:服务端存储重依赖前端的数据,localStorage/sessionStorage存储的数据
服务端存储重依赖前端的数据:例如fabric.js的canvas模板数据,vue-amap的svg路径信息等等。 localStorage/sessionStorage存储的数据: LocalStorage/SessionStorage The keys and the values are always strings。
存储重前端功能的数据
例如Canvas,SVG信息,服务端做持久化。
localStorage/sessionStorage存储的数据
若不转化,也不会报错,会导致存储失效:localStorage.getItem('testObj');// "[object Object]"
查询JSON数据做解析
从服务端接口查询到存储好的Canvas或SVG数据,做解析后传入到fabric.js,vue-amap等进行绘制。
初识JSON.stringify()
- 序列化数据JSON.stringify()方法将一个js对象或者值转换为JSON string类型。
- 过滤数据如果指定replacer为数组或函数,可以对数据进行过滤。
- 格式化数据如果指定space可以对JSON的string进行格式化。
序列化数据
过滤数据
格式化数据
首行缩进两个空格。
- “ ” 两个空格
- 数字2
=>
JSON.stringify(value, [ ,replacer[ ,space]]);语法
参数
value
转化为JSON string的值。
replacer
过滤数据。
函数:replacer可以是函数,返回undefined时不输出数据,非undefined的数据被输出。
字符串数组:replacer可以是数组,一般是string,也可以是number。指定输出JSON的属性白名单。['week', 'month']
。
null或不写:replacer为null或者不写时,所有属性都被输出。null的话一般用于不过滤数据仅设置space的情况。
疑惑:replacer的数组中是数字?[1,2,3,4,5] ?
space
增强可读性的缩进空格或填充字符串。
- 空格锁进数string或number 指明需要插入几个空格。
- number最大最小值空白数。number最大值为10,大于10时也取10。小于1时则代表不需要缩进。
- string最大最小值空白数。string的前10个字符,大于10只取前10个。
- null或不写时不缩进值为null或不写,则默认不锁进。
返回值
JSON string。
异常
TypeError “cyclic object value”
序列化自引用的对象时会报这个错。高德地图的segments就是自引用对象。如何序列化自引用的对象可见下文。
TypeError “BigInt value can't be serialized in JSON”
包含突破Number存储上线的BitInt类型的obj,不能被序列化。
JSON.stringify()描述
常见使用须知
如果想更好的使用JSON.stringify(),下面这些使用须知一定要掌握。
- toJSON方法如果被序列化的value有toJSON()方法,可以在其中定义数据是如何序列化的。
- 数据格式保持序列化后,Boolean,Number,String的数据类型会保留,这种转换是符合传统语义的。
-
不支持转化undefined,function,Symbol这些类型不是有效的JSON值。如果是普通的,直接被忽略;如果在数组中找到时,直接被转成null。JSON.stringify(function(){}) 或JSON.stringify(undefined)返回undefined。
JSON.stringify({ foo: Symbol('foo') });// "{}"
-
Symbol类型作为key时,会被完全忽略。即使在replacer中明确指明。
JSON.stringify({ [Symbol('foo')]: 'foo' });// '{}' JSON.stringify({ [Symbol.for('foo')]: 'foo' }, [Symbol.for('foo')]);// '{}'
- Date类型作为值时。Date的toJSON方法等同于date.toISOString()。
- 特殊的类型:Infinity,NaN, undefined, nullInfinity,NaN, undefined, null都被当作null。
-
特殊的类型:Map,Set, WeakMap,WeakSet所有其他的Object实例(包括Map,Set,WeakMap,WeakSet)都仅有enumerable属性能被序列化。这些值默认是enumerable为false的。
var foo = [['foo',1]];var mp = new Map(foo);JSON.stringify(mp); // "{}"
-
数组的string类型属性是不可被enumerable(列举)的。
let a = ['foo', 'bar']; a['baz'] = 'quux'; // a: [ 0: 'foo', 1: 'bar', baz: 'quux' ]; JSON.stringify(a); // '["foo","bar"]'
-
TypedArray类型可以序列化。
JSON.stringify([new Float32Array([1]), new Float64Array([1])]);// '[{"0":1},{"0":1}]'
究极使用示例:
replacer 参数
replacer可以是function,也可以是array。
function
作为function,有两个参数key 和 value。 默认情况下replacer函数的key为“”,对于对象中的每个值,都会call一次这个函数。
返回值有以下几种情况:
- number number相关联的string属性被添加。
return 123; // "123"
- string 相关联的string属性被添加。
return "foo"; // "foo"
- boolean “true”或”false“被添加。
return true; // "true"
- null "null"被添加。
return null; //"null"
- value 如果是正常的value对象,那么会递归它的属性序列化成JSON string。//
return value;"{"foo":false,"bar":123}"
- undefined 不返回这个属性。//
JSON.stringify({foo: false,bar:undefined},replacer);"{"foo":false}"。
使用强大的undefined是需要注意:
- undefined用过axios的同学这里破案了:当我们的post请求的reqBody包含以undefined为值的参数时,根本不会发到服务端的接口,而null可以。这就是
JSON.stringify()
的功劳。v = JSON.stringify(v)
时,会将undefined类型的值自动过滤掉。
- 不能用replacer去移除array的值。若返回undefined,会返回null。
常用方式
space 参数
- 可以是number 最大10,10个缩进
- 可以是string 最长取前10个,10个char缩进
toJSON()的表现
如果要被字符串化的对象具有一个名为toJSON的属性,其值是一个函数,则该toJSON()方法将自定义JSON字符串化行为:代替被序列化的对象,该toJSON()方法返回的值将被序列化,而不是被序列化的对象。JSON.stringify()调用toJSON一个参数:
- 如果此对象是属性值,则返回属性名称
- 如果它在数组中,则返回数组中的索引(作为字符串)
- 如果JSON.stringify()直接在此对象上调用,则返回一个空字符串
JSON.stringify()序列化循环引用时的问题
自引用对象抛出TypeError
TypeError: Converting circular structure to JSON
JSON.stringify高德地图vue-amap中的自引用对象
路径规划plans的路径SVG信息segments对象。
需求是这样的,前端需要将路线的信息传递给后端,其中包括经纬度数组和SVG数组。 用JSON.stringify()序列化经纬度数组是ok的,但是序列化的SVG数组是自引用的,会报错。(是后来才知道这个SVG数组可以不往服务端存的,不过当时序列化报错是真的懵逼了)
如何序列化自引用对象?
使用Douglas Crockford的cycle.js
它在全局JSON对象上新增了2个方法:
- JSON.decycle(将自引用的属性
myself:obj
替换为myself:{$ref: "$"}
) - JSON.retrocycle
使用示例:
JSON.stringify()序列化具有相同属性相同值但属性顺序不同的对象,结果相等吗?
不相等。
JOSN.stringify()与localStorage的使用示例
localStorage只能存储string类型的数据,因此需要使用JSON.stringify()将对象序列化为JSON string。
回答一下文章开头的问题
为什么这个对象明明有foo属性,序列化后在数据库持久化存储之后,这个属性咋就没了呢?
值为undefined? replacer对它做过滤了? toJSON方法中做过滤了? 属性的enumerable值为false? Symbol? Map?Set?WeakMap?WeakSet? 找原因吧。
为什么axios发送post请求时,如果req的body包含undefined值的参数,在发给服务端的请求中会消失?
源码在buildURL.js 37~56行
打印出的JSON字符串就这么不易读么?除了store成一个variable然后parse之外,能不能不parse就清晰看到它的数据结构呢?
=>
为什么序列化一个对象,它还报错呢?报的还是那种自己引用自己的错误?这种对象不能序列化了吗?
这是因为自引用之后,会限制死循环。 引擎应该是做了特殊的处理,发现这种无限循环时自动抛出异常。 这种对象不能序列化了吗?可以使用cycle.js的decycle(序列化。
参考资料:
- developer.mozilla.org/en-US/docs/…
- github.com/douglascroc…
- developer.mozilla.org/en-US/docs/…
- github.com/axios/axios…
- 微信公众号: 大大大前端