热点聚焦:JSON.stringify()与JSON.parse()没有你想的那样简单
2023-04-17 12:39:45
博客园
(资料图片)
重新学习这两个API的起因在本周五有线上的项目,16:30开始验证线上环境。开始都是顺顺利利,一帆风顺。大概17:50左右,我正在收拾东西。准备下班去王者峡谷骑着我的船溜达一圈。可是天降意外,给我派了一个bug。测试给我说:有一条数据的详情页有数据但是在页面中没有显示数据。不可能,绝对不可能。当时我信誓旦旦的。蛮自信。当时怀疑是这条数据本身就没有详细数据。用户还没有补充详情。但是测试给我发了一张图片。我看见控制台出现红色的 Uncaught SyntaxError映入我的眼球,感觉就像在向我宣战:此时我虚了,感觉十有八九就是一个bug。后来经过排查,发现是 JSON.string()引起的。故而,今天周六简单记录一下 JSON.string它并不是我们想的那样简单。
大家对 JSON.string() 的第一印象是什么?我现在依稀记得:JSON.stringify() 方法将一个 JavaScript 对象或值转换为 JSON 字符串。其他的就没有什么特别的印象。其实,它在对不同类型数据处理时,会做出不同的反应。下面坐好我在峡谷买的船,一起来看一下。
JSON.string()转换的值中有 toJSON() 方法,那么返回值直接替代当前这个对象var obj = { name:"小魔神", like:"喜欢和乌鸦说话", toJSON: function () { return "活下去"; }};var newStr = JSON.stringify(obj);console.log(newStr);此时,你认为输出的值是什么?认真考虑10s。是什么?最后会输出 "活下去"是不是很意外,是不是很惊喜。竟然是这个结果。这的是我们都没有想到对吧?子所以这这个结果:因为:obj这个对象中有 toJSON()方法。那么这个方法的返回值将会替代当前这个对象。所以是 "活下去"
有 toJSON() 方法没有返回值会怎么样?有的小伙伴这个时候就在想了。你说的是因为转换中有 toJSON()方法并且有返回值(retuen)才会替代当前的对象。如果有 toJSON()方法但是没有返回值是不是就不会替换当前这个对象了呢?于是我们写下了这样的代码let obj = { name: "小魔神", like: "喜欢和乌鸦说话", toJSON: function() { console.log("我没有返回值") }};let newStr = JSON.stringify(obj);console.log(newStr);大家觉得是输出什么结果?思考5s钟,你觉得是啥?输出 undefined。为什么是undefined呢?因为函数没有返回值的时候,默认返回 undefined也就是说:toJSON: function() { return undefined console.log("我没有返回值")}你以为 JSON.stringify 的神奇之处只有这点。那你就错了,它有很多我们之前可能没有了解的地方。我们接着往下看,看看还有什么什么黑魔法
无法序列化错误对象,错误对象将会被转为为空对象// 创建了一个错误对象const err = new Error("错的不是我,而是这个世道。")let obj = {name:"小魔神",like:"喜欢和乌鸦说话",err}; console.log(JSON.stringify(obj));// 我们发现 err 这个错误对象变为了空对象 {}是不是觉得 JSON.stringify 有点东西在里面了我们继续往下看
对象中不可枚举的值将不会对其序列化let obj = {name:"小魔神",like:"喜欢和乌鸦说话",}; Object.defineProperty(obj, "name", {value: "魔神",enumerable: false // 将它设置为不可枚举})// name属性将不会被输出。[因为不会对它进行器序列化]console.log(JSON.stringify(obj)); ---这里可以写一是不是觉得 JSON.stringify 越来越有意思了。
NaN 和 Infinity 及 null 都会被当做 null// 1.7976931348623157E+10308 是浮点数的最大上限值 显示为Infinity// -1.7976931348623157E+10308 是浮点数的最小下限值 显示为-Infinityconst obj = { infinityMax: 1.7976931348623157E+10308, infinityMin: -1.7976931348623157E+10308,a: NaN}console.log("obj输出的值是:", JSON.stringify(obj));
日期对象将会对其序列化为字符串stringconst obj = { dateTime: new Date(), name: "小魔神", like: "喜欢和乌鸦说话",}const objCopy = JSON.parse(JSON.stringify(obj));// 发现类型是字符串console.log("类型是", typeof objCopy.dateTime)// 因为是字符串就无法调用原来日期的getTime时间戳了console.log(objCopy.dateTime.getTime())所以在序列化日期对象的时候千千万万要注意。因为它会将日期对象最后变成字符串。从而导致之前的日期方法不能够调用。
循环引用的对象将会抛出错误const obj = {name:"小魔神",like:"喜欢和乌鸦说话",sex:null}obj.sex = obj; //我们这里循环引用了,将会报错const objCopy = JSON.parse(JSON.stringify(obj));console.log("objCopy", objCopy)
undefined、函数、symbol值 在不同的场合将会发生不同的反应undefined、函数[方法]、symbol值在不同的场合,将会发生不同的""化学反应"。在对象中,作为Value值的时候,在序列的时候将会忽略。在对象中,将会被转化为null。单独转化时,将会变为undefined。
undefined、函数、symbol值,在序列化过程中会被忽略 【出现在非数组对象的属性值中时】let person = Symbol("小魔神");const obj = {person,un: undefined,funSy: () => { console.log("前端已死") }}const objCopy = JSON.parse(JSON.stringify(obj));console.log("objCopy",objCopy)我们发现 undefined、函数、symbol值,在序列化过程中会被忽略
undefined、任意的函数、symbol 值将会换成 null(出现在数组中时)let person = Symbol("小魔神");let sayFun = function () { console.log("我太难了") }let arr =[ undefined, person, sayFun]const objCopy = JSON.parse(JSON.stringify(arr));console.log("objCopy",objCopy)所以在进行拷贝的时候,需要特别注意一下。方法[任意的函数]会被丢失。不能调用
函数、undefined,symbol 被单独转换时,会返回 undefinedlet a1 = JSON.stringify(function() {})let a2 = JSON.stringify(undefined)let a3 = JSON.stringify(Symbol("小魔神"))console.log(a1)console.log(a2)console.log(a3)
遨游一圈的感想我们平时在开发中,更多的是使用JSON.string()和JSON.parse()。对我们需要的数据进行拷贝。在拷贝的过程中需要注意以上的情况。否者可能出现翻车。JSON.string()也单独用在 get 请求将数组进行序列化。这个时候各位小伙伴也需要注意一下。避免一些值丢失或者发生变化还有就是将数据存储在localStorage、sessionStorage也会使用JSON.string()我们也需要注意一下
使用JSON.string() 需要注意的点1.使用JSON.string() 转换的值中,如果有 toJSON() 方法,那么返回值直接代替了当前的这个对象 2.有 toJSON() 方法没有返回值会返回 undefined3.无法序列化错误对象,错误对象将会被转为为空对象 4.对象中不可枚举的值将不会对齐序列化 5.NaN 和 Infinity 及 null 都会被当做 null。6.日期对象将会对其序列化为字符串string7.循环引用的对象将会抛出错误8.undefined、任意的函数、symbol 值,在序列化过程中会被忽略【出现在非数组对象的属性值中时】或者被转换成 null(出现在数组中时)。函数、undefined,symbol 被单独转换时,会返回 undefined
简单说下 JSON.parse()我们之前都在介绍 JSON.string(),我们现在简单说下 JSON.parse()。毕竟他们俩是一对好基友JSON.parse() 方法用于将一个 JSON 字符串转换为对象。那什么是 JSON字符串呢?JSON 是一种按照 JavaScript 对象语法的数据格式,这是 Douglas Crockford 推广的。虽然它是基于 JavaScript 语法,但它独立于 JavaScript。这也是为什么许多程序环境能够读取(解读)和生成 JSON。JSON.parse(jsonStr,[function])参数说明:jsonStr:必需, 一个有效的 JSON 字符串。function: 可选,一个转换结果的函数, 将为对象的每个成员调用此函数。
JSON需要注意的点事项1.JSON 是一种纯数据格式,它只包含属性,没有方法。[或者说方法会被丢失]也就是说:如果你原来的某一个对象中包含方法,在使用JSON之后,该方法会被丢失的哈~2.JSON 数据格式为键/值对。 JSON 要求在键值对 key 和 属性名称value周围使用双引号。单引号无效。否者会报错的哈。Uncaught SyntaxError 未捕获的语法错误3.JSON 可以将任何标准合法的 JSON 数据格式化保存,不只是数组和对象。比如,一个单一的字符串或者数字或者一个空数组可以是合法的 JSON 对象。这一点(第3点)很多人认为与第2点互相矛盾。第二点不是说的是键值对key和value吗?怎么单一的字符串和空数组,数字也可以呢?其实没有矛盾,你直接使用 JSON.parse([])这样肯定是不行的。会出现语法错误但是你先使用 JSON.stringify([]) 然后在使用JSON.parse就可以了4.在使用 JSON.parse的使用需要注意第一个参数是否是JSON字符串。否者会出现转化失败
键值对必须使用双引号进行包裹,否则就会报错let jsonStr = "{"name": "张老师", "sex":"男"}";let newArr = JSON.parse(jsonStr)console.log(newArr )// 上面使用的是单引号,会报错// 下面使用的是双引号--不会报错// let jsonStr = "{"a1": "Hello", "b1": "World"}";// let newArr = JSON.parse(jsonStr)// console.log(newArr )ps:键值对必须使用双引号进行包裹这里还隐含了另外一个意思就是说 key和value必须要都要有双引号包裹。否则就会出现语法错误
使用 JSON.parse() 必须要符合JSON字符串从上面的理解中,我们知道了使用JSON.parse() 必须要符合JSON字符串。下面的使用 JSON.parse() 将会报错、非常重要的点:使用JSON.parse() 必须要符合JSON字符串非常重要的点:使用JSON.parse() 必须要符合JSON字符串非常重要的点:使用JSON.parse() 必须要符合JSON字符串重要的事情说三遍
直接转换数组<script>let oldObj= []let arr = JSON.parse(oldObj)console.log("parse", parse )</script>将会报错 Uncaught SyntaxError: Unexpected end of JSON input at JSON.parse ()Uncaught SyntaxError 未捕获的语法错误因为:使用JSON.parse() 必须要符合JSON字符串。然而oldObj= [] 不是一个字符串
非要转换数组let strJSON = JSON.stringify([])let newObj = JSON.parse(strJSON)console.log("newObj", newObj ) // 输出的是 []我们先使用JSON.stringify([])将它转化为JSON字符串就可以了
JSON.parse() 不允许用逗号作为结尾JSON.parse("[10, 20, 30, 40, ]");JSON.parse("{"name1" : "澹台烬", }");
尾声1.JSON 是一种纯数据格式,它只包含属性,没有方法。2.JSON 要求在键值对 key 和 属性名称value周围使用双引号。单引号无效。3.JSON 可以将任何标准合法的 JSON 数据格式化保存。如:数组,对象,单一的字符串或者数字4.JSON.parse() 不允许用逗号作为结尾特别提醒:在使用 JSON.parse的使用需要注意第一个参数是否是JSON字符串。如果你觉得我写的还不错:请我点一个推荐或者打赏。感谢各位大佬