JSON.parse(JSON.stringify(obj))
先了解一下基本的用法
JSON.stringify(value[, replacer [, space]])
value
将要序列化成 一个 JSON 字符串的值。
replacer
可选
space
可选
一个表示给定值的 JSON 字符串。
TypeError ("cyclic object value")
(循环对象值)TypeError ("BigInt value can't be serialized in JSON")(BigInt 值不能 JSON 序列化)
.value
可以转换对象,也可以转换 简单类型的值;replacer
有三种情况,函数,数组,空;space
如果为字符串,则会直接替换掉空格;个人总结练习
// 1.1.1 转换对象
console.log(
JSON.stringify({ name: 'tomato' }),
typeof JSON.stringify({ name: 'tomato' })
) // '{"name":"tomato"}' string
// 1.1.2 转换数组
console.log(
JSON.stringify([1, 2, 3]),
typeof JSON.stringify({ name: 'tomato' })
) // '[1,2,3]' string
// 1.1.1 function 返回的是undefined
// 1.1.3 转换函数
console.log(
JSON.stringify(function () {
console.log(123)
}),
typeof JSON.stringify(function () {
console.log(123)
})
) // undefined undefined
// 1.2 转换简单类型
console.log(JSON.stringify(1), typeof JSON.stringify(1)) // '1' string
console.log(JSON.stringify('1'), typeof JSON.stringify('1')) // "1" string
console.log(JSON.stringify(false), typeof JSON.stringify(false)) // 'false' string
console.log(JSON.stringify(null), typeof JSON.stringify(null)) // 'null' string
// 1.2.1 undefined 和 Symbol有点特殊,返回的是 undefined
console.log(JSON.stringify(undefined), typeof JSON.stringify(undefined)) // undefined undefined
console.log(JSON.stringify(Symbol('a')), typeof JSON.stringify(Symbol('a'))) // undefined undefined
// 1.2.2 BigInt 类型的会报异常
// console.log(JSON.stringify(10n)) // TypeError: Do not know how to serialize a BigInt
// 2. replacer 的三种情况:
// 2.1 replacer 是函数的情况
console.log(
JSON.stringify({ name: 'tomato', sex: 'boy', age: 18 }, (key, value) => {
return typeof value === 'number' ? undefined : value
})
)
// '{"name":"tomato","sex":"boy"}'
// 2.2 replacer 是数组的情况
console.log(JSON.stringify({ name: 'tomato', sex: 'boy', age: 18 }, ['name']))
// '{"name":"tomato"}'
// 2.2 null 或者 未提供
console.log(JSON.stringify({ name: 'tomato', sex: 'boy', age: 18 }))
// '{"name":"tomato","sex":"boy","age":18}'
// 3. 指定space(美化输出)
console.log(JSON.stringify({ x: 5, y: '你好' }, null, 1))
/*
{
"x": 5,
"y": "你好"
}
*/
console.log(JSON.stringify({ x: 5, y: '你好' }, null, 100))
/*
{
"x": 5,
"y": "你好"
}
*/
console.log(JSON.stringify({ x: 5, y: '你好' }, null, 'tomato12345678'))
/*
{
tomato1234"x": 5,
tomato1234"y": "你好"
}
*/
undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。函数、undefined 被单独转换时,会返回 undefined
转换值如果有 toJSON() 方法,该方法定义什么值将被序列化。
非数组对象的属性不能保证以特定的顺序出现在序列化后的字符串中。
布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值。
对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误。
所有以 symbol 为属性键的属性都会被完全忽略掉,即便 replacer 参数中强制指定包含了它们。
Date 日期调用了 toJSON() 将其转换为了 string 字符串(同 Date.toISOString()),因此会被当做字符串处理。
NaN 和 Infinity 格式的数值及 null 都会被当做 null。
其他类型的对象,包括 Map/Set/WeakMap/WeakSet,仅会序列化可枚举的属性。
依次解读:
虽然看着文字有点多,但是不是很难,九大特性都要记住,现在我们依次去实验一下。
特性一我们又可以对它进行拆分,如下:
undefined
、任意的函数以及 symbol 值,会被忽略undefined
、任意的函数以及 symbol 值, 转换成 null(出现在数组中时)。// 一.非数组对象
let obj = {
name: 'tomato',
// 空字符串不会被忽略
other: '',
// 函数 会被忽略
say: function() {
console.log('say')
},
// Symbol 会被忽略
sym: Symbol('123'),
// undefined 会被忽略
un: undefined
}
// {"name":"tomato","other":""}
// 二.数组
let a = [
'tomato',
'',
function() {
console.log('say')
},
Symbol('123'),
undefined
]
// ["tomato","",null,null,null]
// 三.单独转换 返回undefined
console.log(
JSON.stringify(function() {
console.log(123)
}),
typeof JSON.stringify({ name: 'tomato' })
) // undefined string
console.log(JSON.stringify(undefined), typeof JSON.stringify(undefined)) // undefined undefined
console.log(JSON.stringify(Symbol('a')), typeof JSON.stringify(Symbol('a'))) // undefined undefined
如果转换的值存在 toJSON() 方法,那么序列化的是这个函数的返回值。
function tomato() {
console.log('你好')
}
// 1. 单独转换函数==> undefined
console.log(JSON.stringify(tomato)) // undefined
// 2.存在 toJSON属性为一个返回值为 '起飞' 的函数
tomato.toJSON = function() {
return '起飞'
}
console.log(JSON.stringify(tomato)) // '起飞'
// 3.存在 toJSON属性为一个返回值为 函数 的函数
tomato.toJSON = function() {
return function() {
return '第二个函数'
}
}
console.log(JSON.stringify(tomato)) // undefined
// 4.存在 toJSON属性为一个返回值为 字符串
tomato.toJSON = '我是番茄,如果toJSON不是方法'
console.log(JSON.stringify(tomato)) // undefined
// 5.原型上有 toJSON方法
function T2() {
console.log('新建一个函数')
}
T2.prototype.toJSON = function() {
return '原型上的toJSON方法'
}
let t2 = new T2()
console.log(JSON.stringify(t2)) // 原型上的toJSON方法
// 个人总结: 如果转换值自身或者原型上存在toJSON()方法; 最终结果为 将toJSON()的返回值-序列化后的结果
这个怎么解释呢。因为在 非数组对象中 undefined
, function
, Symbol
,会被忽略,所以顺序不能保证
let obj = {
name: '番茄',
say() {
console.log('‘你好呀')
},
other: '理论上我是第三个属性'
}
console.log(JSON.stringify(obj))
// {"name":"番茄","other":"理论上我是第三个属性"}
let arr = [
'番茄',
function() {
console.log('‘你好呀')
},
'理论上我是第三个属性'
]
console.log(JSON.stringify(arr))
// ["番茄",null,"理论上我是第三个属性"]
console.log(typeof JSON.stringify(new String('123')), JSON.stringify(new String('123'))) // string "123"
console.log(typeof JSON.stringify(new Number(123)), JSON.stringify(new Number(123))) // string "123"
console.log(typeof JSON.stringify(new Boolean(false)), JSON.stringify(new Boolean(false))) // string "false"
TypeError
let obj = {
name: '番茄'
}
obj.like = obj
console.log(obj) // { name: '番茄', like: [Circular] }
console.log(JSON.stringify(obj)) // TypeError: Converting circular structure to JSON
console.log(JSON.stringify(10n)) // TypeError: Do not know how to serialize a BigInt
let sy = Symbol('key')
let obj = {
// 特性一 Symbol值会被忽略
name: Symbol('to'),
// 特性六
[sy]: '123'
}
console.log(JSON.stringify(obj)) // {}
console.log(
JSON.stringify(obj, (key, value) => {
if (typeof key === 'string') {
return '我是字符串' + key
}
if (typeof key === 'symbol') {
return '我是symbol' + key
}
})
) // 我是字符串
let obj = {
date: new Date(),
date2: new Date().getTime(),
date3: '123'
}
console.log(JSON.stringify(obj, null, 4))
/*
{
"date": "2022-07-19T09:15:07.969Z",
"date2": 1658222107969,
"date3": "123"
}
*/
/*
!! 这里啰嗦一句,之前使用axios,调用接口
传参就是:
let params = {
time : new Date()
}
实际发送给后端的数据格式 : "2022-07-19T09:15:07.969Z"
当时是百思不得其解,现在就一目了然了,大概率axios源码中,使用了JSON.stringify()序列化了参数
*/
console.log(JSON.stringify({
n1: NaN,
n2: Infinity,
n3: null
}))
// '{"n1":null,"n2":null,"n3":null}'
let enumerableObj = {}
Object.defineProperties(enumerableObj, {
name: {
value: '番茄',
enumerable: true
},
sex: {
value: 'boy',
enumerable: false
}
})
console.log(JSON.stringify(enumerableObj))
// '{"name":"番茄"}'
这里借鉴的别人手写的代码。写的很好就直接
放一下原文链接:原文链接
// 写一个简易版本的 JSON.stringify ==> myStringify方法 (忽略后面两个参数了)
function myStringify(data) {
// 定义判断是否是循环引用的方法
const isCyclic = (obj) => {
let stackSet = new Set()
let detected = false
// 检测 对象 是否循环引用
const detect = (obj) => {
// 不是对象直接 return
if (obj && typeof obj !== 'object') {
return
}
// 当要检查的对象已经存在于stackSet中时,表示存在循环引用
if (stackSet.has(obj)) {
return (detected = true)
}
// 将当前obj存储到stackSet
stackSet.add(obj)
for (let key in obj) {
// 对obj下的属性进行递归检测
if (obj.hasOwnProperty(key)) {
detect(obj[key])
}
}
// 平级检测完成之后,将当前对象删除,防止误判
/*
例如:对象的属性指向同一引用,如果不删除的话,会被认为是循环引用
let tempObj = {
name: '前端胖头鱼'
}
let obj4 = {
obj1: tempObj,
obj2: tempObj
}
*/
stackSet.delete(obj)
}
detect(obj)
return detected
}
// 特性五 循环引用
if (isCyclic(data)) {
throw new TypeError('Converting circular structure to JSON')
}
// 特性五 BigInt
if (typeof data === 'bigint') {
throw new TypeError('Do not know how to serialize a BigInt')
}
const type = typeof data
const commonKeys1 = ['undefined', 'function', 'symbol']
const getType = (s) => {
return Object.prototype.toString
.call(s)
.replace(/\[object (.*?)\]/, '$1')
.toLowerCase()
}
// 非对象
if (type !== 'object' || data === null) {
let result = data
// 特性八: NaN 和 Infinity 格式的数值及 null 都会被当做 null。
if ([NaN, Infinity, null].includes(data)) {
result = 'null'
// 特性一 ③:
// `undefined`、`任意的函数`以及`symbol值`被`单独转换`时,会返回 undefined
} else if (commonKeys1.includes(type)) {
// 直接得到undefined,并不是一个字符串'undefined'
return undefined
} else if (type === 'string') {
result = '"' + data + '"'
}
return String(result)
} else if (type === 'object') {
// 特性二:
// 转换值如果有 toJSON() 方法,该方法定义什么值将被序列化
// 特性七:
// Date 日期调用了 toJSON() 将其转换为了 string 字符串(同Date.toISOString()),因此会被当做字符串处理。
if (typeof data.toJSON === 'function') {
return myStringify(data.toJSON())
} else if (Array.isArray(data)) {
let result = data.map((it) => {
// 特性一 ②:
// `undefined`、`任意的函数`以及`symbol值`出现在`数组` 中时会被转换成 `null`
return commonKeys1.includes(typeof it) ? 'null' : myStringify(it)
})
return `[${result}]`.replace(/'/g, '"')
} else {
// 特性四:
// 布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值。
if (['boolean', 'number'].includes(getType(data))) {
return String(data)
} else if (getType(data) === 'string') {
return '"' + data + '"'
} else {
let result = []
// 特性八
// 其他类型的对象,包括 Map/Set/WeakMap/WeakSet,仅会序列化可枚举的属性
Object.keys(data).forEach((key) => {
// 特性三:
// 所有以symbol为属性键的属性都会被完全忽略掉,即便 replacer 参数中强制指定包含了它们。
if (typeof key !== 'symbol') {
const value = data[key]
// 特性一 ①:
// `undefined`、`任意的函数`以及`symbol值`,出现在`非数组对象`的属性值中时在序列化过程中会被忽略
if (!commonKeys1.includes(typeof value)) {
result.push(`"${key}":${myStringify(value)}`)
}
}
})
return `{${result}}`.replace(/'/, '"')
}
}
}
}
myStringify()
在 JSON.parse(JSON.stringify(obj))
的时候经常会遇到这种报错;
Uncaught SyntaxError: Unexpected token u in JSON at position 0
at JSON.parse ()
at :1:6
首先考虑 JSON.parse()
参数为undefined或 空字符串的情况:
let obj = {
err: new Error('出错啦'),
regx: new RegExp('aaa')
}
console.log(JSON.stringify(obj))
// {"err":{},"regx":{}}